Featured image of post Linux网络内核收发包过程

Linux网络内核收发包过程

飞哥开发内功修炼(https://github.com/yanfeizhang/coder-kung-fu)的学习笔记,涉及内核的收发包原理

内核收包路径

  1. 系统初始化化时,网卡驱动程序向系统内核在内存中申请一块Ring Buffer,用于存储从外部网络接受到的数据包;
  2. 网卡驱动程序将申请到的Ring Buffer地址告知网卡;
  3. 当数据帧从外部网络到达网卡时,网卡把帧通过DMA拷贝存放到Ring Buffer中;
  4. 网卡产生硬中断(通过给CPU物理引脚施加电压变化实现),告知CPU有数据帧到达;
  5. 当CPU接收到中断请求后,调用网络设备驱动注册的中断处理函数;
  6. 中断处理函数发出软中断请求(给内存中的一个变量赋予二进制值以标识有软中断发生),然后释放CPU资源;
  7. ksoftirqd检测到有软中断请求到达,调用网卡驱动注册的poll函数开始轮询收包,并且封装为sk_buff,然后递交给网络协议栈进行处理;
  8. 协议栈处理完成后数据就进入用户态的对应进程,进程就可以操作数据了。

DMA

  1. 系统启动时将网卡初始化,在内存中分配空间给Ring Buffer;
  2. 初始状态下,Ring Buffer中存放的每个元素Packet Descriptor指向一个DMA Buffer(用于存储从外部网络接受到的数据包),状态均为ready;
  3. 当网卡接收到数据时,DMA负责在Ring Buffer上按顺序找到下一个状态为ready的Packet Descriptor,将数据存入该Packet Descriptor指向的DMA Buffer中;
  4. 修改Packet Descriptor,标记被写入数据的DMA Buffer为used状态;
  5. 当DMA处理完数据之后,网卡会触发一个硬中断让CPU去处理接收到的数据。

因此当Ring Buffer满的时候,新接收到的数据包将被丢弃。

中断处理

硬中断在整个操作系统中拥有最高优先级,当一个硬中断到来后,CPU必须马上停止当前正在执行的程序,转而执行中断处理程序。如果该中断处理程序是一段耗时长的逻辑,就会导致CPU无法释放。为此引入了软中断:网卡发出硬中断信号,CPU收到后调用对应的中断处理程序,该中断处理程序仅简单的发起软中断请求,而软中断对应的处理函数就可以让CPU按照自己的调度策略去执行。

ksoftirqd负责处理软中断,其不断的循环检测中断标记,当检测到软中断时调用驱动注册的poll函数。poll函数会遍历Ring Buffer中的Packet Descriptor,查看哪些Packet Descriptor指示有新数据包。如果发现有新接收到的数据包,就在内核空间内存分配一个新的sk_buff,将DMA Buffer中的数据拷贝(或映射,传统驱动大多采用复制方式,XDP、DPDK或者某些支持零拷贝的驱动会采用映射方式)到该sk_buff的数据区域,随后将sk_buff传递给协议栈进行进一步的处理。

内核发包路径

当协议栈准备好数据包时,驱动的发送函数被调用,驱动检查Ring Buffer(根据发送与接收分为RX Ring和TX Ring),并将sk_buff中的数据映射到DMA Buffer中、更新Packet Descriptor,随后通过写寄存器或触发中断以告知网卡有数据包要发送,网卡开始从Packet Descriptor指向的内存读取数据并发送出去。

网卡发送完数据后,会通过硬中断通知驱动程序(同样是先硬中断,随后是软中断)回收Ring Buffer中的Packet Descriptor、解除DMA映射、释放sk_buff资源。

Licensed under CC BY-NC-SA 4.0
皖ICP备2025083746号-1
公安备案 陕公网安备61019002003315号



使用 Hugo 构建
主题 StackJimmy 设计