基础-Linux收发网络包

Linux系统是怎么收发网络包的?

1. 网络模型

1.1 四层网络模型

  • 应用层:负责向用户提供一组应用程序,比如HTTP,DNS,FTP;
  • 传输层:负责端到端的通信,比如TCP,UDP;
  • 网络层:负责网络包的封装,分片,路由,转发,比如IP,ICMP;
  • 网络接口层:负责网络包在物理网络中的传输,比如网络包的封帧,MAC寻址,差错检测,通过网卡传输网络帧;

1.2 七层网络模型

2. Linux接收网络包流程

通过网卡接收数据,网卡时计算机中的一个硬件,专门负责接收和发送网络包;

  1. 当网卡接收到一个网络包后,通过DMA技术将网络包写入到指定的内存地址即Ring Buffer,随后告知操作系统网络包以到达;告知方式有两种:
    • 触发中断:网卡收到网络包触发中断,CPU去处理网络包,处理完之后再回去继续执行之前的任务,缺点是需要频繁触发中断,导致系统效率变低;
    • NAPI机制:混合中断和轮询,采用中断唤醒数据接收服务程序,然后使用poll的方法轮询数据;
      • 暂时屏蔽中断,表示已经知道当前内存中有数据,并告知网卡下次接收到数据直接写入内存即可,不用再触发中断,避免频繁触发中断;
      • 接着发起软中断,执行软中断处理程序,并恢复刚才屏蔽的中断;
  2. 内核中的ksoftirqd线程专门用于处理软中断,当其收到软中断后,开始轮询处理数据,从Ring Buffer中获取一个数据帧,用sk_buff表示,从而可以作为一个网络包,交给网络协议栈进行逐层处理;
  3. 数据到达网络接口层,在这一层检查报文的合法性,如果不合法则丢弃,如果合法则找到该网络包的上层协议类型,比如是IPv4还是IPv6,并去掉帧头和帧尾,然后交给网络层;
  4. 数据到达网络层后,取出IP头,判断网络包下一步走向,是交给上层处理还是转发出去;当确认这个网络包是发给自己的时候,会从IP头中查看上层协议是TCP还是UDP,并去掉IP头,然后交给传输层;
  5. 数据到达传输层后,取出TCP头或UDP头,根据四元组(源IP,目的IP,源端口,目的端口)作为标识,找出对应的socket,并把数据发到socket的接收缓冲区中;
  6. 最后,应用层程序调用socket接口,将内核的socket接收缓冲区的数据拷贝到应用层的缓冲区,然后唤醒应用进程;

3. Linux发送网络包流程

  1. 应用程序调用socket发送数据包的接口(系统调用),从用户态陷入内核态中的socket层,内核申请一个sk_buff内存,将用户待发送的数据拷贝的sk_buff中,并将其加入到发送缓冲区;
  2. 网络协议栈从socket发送缓冲区中取出sk_buff,并按照TCP/IP协议栈从上到下逐层处理;
  3. 如果使用的是TCP,需要先拷贝一个新的sk_buff副本(TCP支持丢失重传,当收到对方的ACK之后,会将这个副本删除);
  4. 对sk_buff填充TCP头,并将数据交给网络层;
  5. 网络层在收到数据包之后,选取路由,填充IP头,netfilter过滤,对超过MTU大小的数据包进行切片,处理完之后将数据包交给网络接口层;
  6. 网络接口层通过ARP协议获得下一跳的MAC地址,并对sk_buff填充帧头和帧尾,接着将sk_buff放到网卡的发送队列中;
  7. 之后,触发软中断告诉网卡驱动程序,驱动程序会从发送队列中读取sk_buff,将sk_buff挂到Ring Buffer中,接着将sk_buffer数据映射到网卡可访问的内存DMA区域,最后触发真实的发送;
  8. 完成发送后,网卡设备会触发一个硬中断来释放内存,即sk_buff副本内存以及清理Ring Buffer内存;
  9. 最后当收到TCP报文的ACk应答时,传输层再释放原始sk_buff;

4. 发送数据包涉及几次内存拷贝

3次:

  • 第一次:调用发送数据的系统调用时,内核申请内核态的sk_buff内存,将用户待发送的数据拷贝的sk_buff内存,并将其加入到发送缓冲区;
  • 第二次:在使用TCP传输协议的时候,需要拷贝一个sk_buff用来支持丢失重传;
  • 第三次:当IP层发现sk_buff大于MTU时,会再申请额外的sk_buff并将原来的sk_buff拷贝分为多个小的sk_buff;

基础-Linux收发网络包
http://example.com/2025/05/24/NetworkCommunications/基础-Linux收发网络包/
作者
ZhangHangming
发布于
2025年5月24日
许可协议