网卡到协议栈边界发生的事情:
数据包的接受过程
数据包的接收,从下往上经过了三层:网卡驱动、系统内核空间,最后到用户态空间的应用。
现在来详细解释下:
- 当一个新的数据包到达,
NIC
(network interface controller
)调用DMA
engine
,通过Ring
Buffer
将数据包放置到内核内存区。 - 一旦数据包被成功接收,
NIC
发起中断,由内核的中断处理程序将数据包传递给IP
层。 - 经过
IP
层的处理,数据包被放入队列等待TCP
层处理。每个数据包经过TCP
层一系列复杂的步骤,更新TCP
状态机, - 最后到达
recv
Buffer
,等待被应用接收处理。
我现在解释几点:
Linux
内核使用sk_buff
(socket kernel buffers
)数据结构描述一个数据包。Ring Buffer
的大小固定,它不包含实际的数据包,而是包含了指向sk_buff
的描述符。当Ring ``Buffer
满的时候,新来的数据包将给丢弃。- 从
NIC
中内存的package
数据可以不经过CPU
直接拷贝到Kernel
中的内存,这个过程就是
DMA
:Directional Memory Access
,主要用于快速数据交换。 - 从上图中可以看到
DMA
操作了 kernel 中的一块内存,这块内存其实是Device Driver
注册的时候向Kernel
提前申请的。 - 最后需要注意,数据包到达
recv
Buffer
,TCP
就会回ACK
确认,既TCP
的ACK
表示数据包已经被操作系统内核收到,但并不确保应用层一定收到数据(例如这个时候系统crash
),因此一般建议应用协议层也要设计自己的确认机制。
数据包的发送过程
上图中红色的线条表示发送数据包的过程,和接收数据的路径相反,数据包的发送从上往下也经过了三层:用户态空间的应用、系统内核空间、最后到网卡驱动。
- 应用先将数据写入
TCP
send buffer
,TCP
层将send buffer
中的数据构建成数据包转交给IP
层。 IP
层会将待发送的数据包放入队列QDisc
(queueing discipline
)。- 数据包成功放入
QDisc
后,指向数据包的描述符sk_buff
被放入Ring
Buffer
输出队列,随后网卡驱动调用DMA
engine
将数据发送到网络链路上。