一、协议栈分层结构

在这里插入图片描述

  • 应用程序(Application):比如浏览器、ssh、httpd,产生或消费“应用数据”。
  • 系统调用(syscall)send/recv, write/read 等把应用和内核网络栈连接起来。
  • socket 接口(socket):应用的抽象(struct socket / struct sock),负责把应用语义映射成网络层/传输层要处理的数据结构。
  • 传输层(TCP / UDP)
    • TCP:面向连接、可靠、拥塞控制、重传。
    • UDP:无连接、不可靠、低开销(没有重传/拥塞控制逻辑)。
  • 网络层(IP):路由、分片/重组、TTL、IP 校验等。
  • 链路层(MAC/以太网):同一物理/链路上的帧封装(源/目的 MAC、以太网类型、VLAN 等)。
  • 驱动与网卡(NIC):网卡通过 DMA 在内存和物理链路之间搬运数据;驱动负责设置 DMA 描述符、响应中断、交互内核。

作用:每层只关心自己头部的读写与语义转换,数据在内核内部以统一载体(sk_buff)传递。

二、数据包

  • 各层对数据的称呼

    • 用户层:data(应用字节流/报文)
    • TCP 层:segment(TCP 段,含端口、序号、ACK 等)
    • IP 层:packet(IP 包,含源/目的 IP,TTL 等)
    • 链路层:frame(以太网帧,含源/目的 MAC、类型、FCS)
  • 内核统一抽象:sk_buff(skb)

    • skb 是 Linux 网络栈内的“万能包”结构(struct sk_buff),它包含:
      • 指向数据缓冲区的指针(head、data、tail、end)
      • 长度信息(len、data_len)
      • 指向各层头部的指针(mac_header, network_header, transport_header
      • 指向接收/发送的 devstruct net_device
      • 协议类型(protocol),校验状态(ip_summed
      • 控制块(skb->cb)为各协议层临时使用
      • 分片信息(skb_shinfo())用于零拷贝大包、GSO 等
  • 作用:从驱动到用户空间,所有层通过修改 skb 的头部指针、长度来封装/剥离首部。

三、接收网络数据包流程(RX)

  1. 网卡接收数据并DMA写入内存

    • 数据到达网卡:当一台主机的网卡收到网络上的以太网帧时,帧首先进入网卡的硬件接收缓存。
    • DMA传输
      • 网卡不会通过CPU一条条指令把数据搬到内存,而是通过DMA控制器直接将数据写入内核预先分配好的 环形缓冲区(ring buffer) 中。
      • 这样避免了CPU的频繁参与,节省了宝贵的处理时间。
    • 结果:数据已经进入内存,等待后续处理。
  2. 网卡触发硬件中断

    • 中断信号:当网卡完成数据写入后,会向CPU发出一个 硬件中断 信号,通知“有新数据了”。
    • CPU响应:CPU暂停当前正在执行的任务,跳转到网卡驱动注册的 中断服务例程(ISR)**。
    • ISR的任务:只做轻量级的工作(例如标记数据已到来、触发软中断),避免长时间占用CPU。
  3. 中断抑制与合并

    • 问题:如果网卡收到的数据包很多,每个包都触发一次中断,会让CPU忙于处理中断,效率极低。
    • 解决方法
      1. 中断合并:网卡会把多个数据包凑在一起后,再触发一次中断。这样能显著减少中断次数。

      2. NAPI(New API)机制

        • 在低流量时:继续使用中断方式,保证低延迟。
        • 在高流量时:切换为 轮询模式,由内核主动批量从网卡环形缓冲区中取数据,避免被中断“淹没”。
  4. 软中断处理

    • 触发软中断:硬件中断处理函数不会直接处理所有数据,而是触发一个 软中断(NET_RX_SOFTIRQ)。
    • 执行主体:软中断通常由 ksoftirqd 内核线程 来执行。
      • 从网卡的 ringbuffer 中批量取出数据。
      • 把每个数据包包装成内核的 skb(socket buffer) 结构,以便后续协议栈处理。
  5. 链路层处理

    • 解析以太网帧头:从 skb 中读取 以太网头部,提取源MAC地址、目的MAC地址、协议类型字段。
    • 协议分流
      • 如果协议字段是 0x0800 -> IPv4
      • 如果是 0x86DD -> IPv6
      • 其他类型(如ARP)则交给对应模块处理。
    • 剥离头部:去掉以太网帧的头部,只留下 IP 层以上的数据。
    • 校验与过滤:可能进行简单的错误检查,丢弃无效或不需要的数据帧。
  6. IP层与传输层处理

    • IP层

      • 校验:检查IP头部的校验和是否正确。
      • 分片重组:如果一个数据包被拆成多个片段,这里会进行重组。
      • 路由判定:决定这个包是交给本机(上交传输层),还是需要转发出去(作为路由器)。
    • 传输层(TCP/UDP)

      • 根据 五元组(源IP、目的IP、源端口、目的端口、协议号) 定位到正确的 socket。
      • TCP 会进行序列号、确认号检查,保证顺序和可靠性。
      • UDP 直接把数据交给应用层,不做额外保证。
    • 放入 socket 缓冲区

      • 通过内核的 socket 结构体,数据被写入到该 socket 的接收缓冲区(sk_rcvbuf)。
      • 应用程序稍后就能读取这些数据。
  7. 应用层读取数据

    • 系统调用接口:应用程序通过 recv()read() 等调用从 socket 中获取数据。
    • 阻塞与非阻塞
      • 如果 socket 是 阻塞模式,而缓冲区里暂时没有数据,应用进程会 进入睡眠状态,直到有新数据到达再被唤醒。
      • 如果是 非阻塞模式,调用会立即返回(可能返回“无数据可读”错误)。
    • 最终结果:应用层获得完整的网络数据,可以继续处理(例如HTTP解析、数据库协议处理等)。

四、发送网络数据包流程(TX)

  1. 应用层写入 socket 缓冲区

    • 应用程序调用 send() / write() 系统调用,数据从用户空间复制到内核空间。
    • TCP:数据会进入发送缓冲区(sk_sndbuf),暂时存放,等待后续分段和发送。
    • UDP:一般直接封装成 skb,立即进入发送路径,不需要复杂的缓冲管理。
    • 应用层只需把数据交给内核,后续传输由内核和协议栈全权负责。
  2. 协议栈构造 skb

    • 内核将待发送数据组织成 skb(socket buffer,对网络数据包的统一抽象)。
    • 如果数据超过 MSS(最大报文段长度),会进行分段(Segmentation)。
    • 可启用 GSO/ TSO,让内核或网卡自动完成分段,减少 CPU 负担。
    • 保证数据按链路和协议限制分块,同时优化性能。
  3. 逐层封装

    • TCP 层:为数据段加上 TCP 头部(序列号、确认号、端口号、校验和等)。
    • IP 层:添加 IP 头部(源/目的 IP 地址、TTL、协议号等),确定路由。
    • 链路层:加上 MAC 头部(源 MAC、目的 MAC、协议类型)以及帧尾(FCS 校验)。
    • 每一层都添加必要的信息,最终生成完整的以太网帧,能在物理网络上传输。
  4. 队列调度与流量控制

    • 封装好的 skb 进入 qdisc(排队规则),用于流量整形、限速、优先级调度等。
    • 内核调用 dev_queue_xmit(),将数据交给对应网卡驱动。
    • 实现 QoS(服务质量)、公平调度,避免某些流量独占带宽。
  5. 驱动处理与 DMA 传输

    • 网卡驱动将 skb 映射到 TX ring buffer(发送队列,DMA 可见的区域)。
    • 网卡通过 DMA 从内存中直接读取数据,而不需要 CPU 搬运。
    • 网卡把数据帧发送到物理链路(如以太网、Wi-Fi)。
    • DMA 提高效率,CPU 只负责控制,不参与数据搬运。
  6. 发送完成中断与资源回收

    • 网卡完成发送后,触发一个硬件中断通知 CPU。
    • 驱动程序回收 TX ring 中的资源:释放已发送的 skb,解除 DMA 映射。
    • 避免内存泄漏,保证发送队列能够被循环利用。
  7. TCP 确认与缓冲区管理

    • 对于 TCP 连接:当接收方返回 ACK 确认报文,发送端内核会:
      • 释放已确认的数据,腾出 sk_sndbuf 空间。
      • 更新拥塞控制算法(如 Reno、CUBIC)和发送窗口大小。
    • 如果发生丢包或超时,TCP 会触发重传机制。
    • 保证可靠传输,动态调整传输速率,适应网络状况。
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐