网络包的接收流程
- 网络数据包(Frame)到达网卡,按照FIFO顺序存入网卡的接受队列;
- 网卡通过DMA方式将接受队列的数据拷贝到内核缓冲区sk_buffer;
- 当数据拷贝完成后,网卡通过硬件中断(IRQ)通知CPU有新的网络数据到来,CPU执行驱动程序(NIC Driver)的处理逻辑:
3.1 禁用网卡的中断。这样做的目的是避免CPU被频繁的中断,当网卡接收到新的数据时直接写内存就可以了,不要再通知CPU;
3.2 启动软中断。这步结束后,硬件中断处理函数就结束返回了。由于硬中断处理程序执行的过程中不能被中断,所以如果它执行时间过长,会导致CPU没法响应其它硬件的中断,于是内核引入软中断,这样可以将硬中断处理函数中耗时的部分移到软中断处理函数里面来慢慢处理。 - 软中断处理,将sk_buffer中的数据转换为skb格式。内核中的ksoftirqd进程专门负责软中断的处理,当它收到软中断后,就会调用相应软中断所对应的处理函数,对于网卡驱动模块抛出的软中断,ksoftirqd的最终处理逻辑是将网卡写入到内存中的数据包转换成内核网络模块能识别的skb格式,然后将数据交给协议栈处理;
- TCP/IP协议层逐层处理。
5.1 在链路层检查报文的合法性,找出上层协议的类型(比如 IPv4 还是 IPv6),再去掉帧头、帧尾,然后交给网络层。
5.2 网络层取出 IP 头,判断网络包下一步的走向,比如是交给上层处理还是转发。当网络层确认这个包是要发送到本机后,就会取出上层协议的类型(比如 TCP 还是 UDP),去掉 IP 头,再交给传输层处理。
5.3 传输层取出 TCP 头或者 UDP 头后,根据 < 源 IP、源端口、目的 IP、目的端口 > 四元组作为标识,找出对应的 Socket,并把数据拷贝到 Socket 的接收缓存中。
待内存中的所有数据包被处理完成后(即poll函数执行完成),启用网卡的硬中断,这样下次网卡再收到数据的时候就会通知CPU; - 应用程序通过read()从套接字缓冲区读取数据;
网卡数据如何写入到内存?
- 驱动程序启动时创建和初始化Ring Buffer,存储的是sk_buff缓冲区的描述符(物理地址和大小等);
- 当网络报到达时,从Ring Buffer获取指向的sk_buff,通过DMA将数据写入该地址;
- 等sk_buff中的数据交由上层协议栈处理后,Ring Buffer中的描述更新为新分配的sk_buff;
overruns VS dropped
二者是对网络包接受过程的不同阶段的错误统计,overruns反映了网络包进入网卡的接受队列过程中发生的错误数;dropped反映了数据从网卡拷贝到内存过程中发生的错误数。可通过ifconfig命令查看监控指标。
On Linux ifconfig gets the raw data statistics from /proc/net/dev. The values there (with ifconfig destination in brackets added) are described by an article Exploring the /proc/net/ Directory as:
errs (Errors): The total number of transmit or receive errors detected by the device driver.
drop (Dropped): The total number of packets dropped by the device driver.
fifo (Overruns): The number of FIFO buffer errors.
frame (Frame): The number of packet framing errors.
carrier (Carrier): The number of carrier losses detected by the device driver.
网络包的发送流程
- 应用程序调用 Socket API发送网络包,套接字层把数据包放到 Socket 发送缓冲区中;
- TCP/IP协议栈处理。
- 协议栈处理完成后产生软中断通知驱动程序,发送队列中有新的网络帧需要发送;
- 最后,驱动程序通过 DMA ,从发包队列中读出网络帧,并通过物理网卡把它发送出去。
参考: