现在的位置: 首页 > 综合 > 正文

tcp/ip stack 中的数据包队列

2013年04月02日 ⁄ 综合 ⁄ 共 1231字 ⁄ 字号 评论关闭

TCP/IP 是一种存储/转发的协议,因此,在 stack 中必然存在数据包的队列(sk_buff queue)。

正是这些队列,将 stack 的处理逻辑比较清晰的划分成几个部分。

 

数据包接收路径上的队列:

 

1、网络层队列:softnet_data[this_cpu]. input_pkt_queue

 

每个 CPU 有一个这样的队列。此队列将网卡驱动的处理逻辑与协议分析(网络层、传输层)的处理逻辑区分开来。

 

当网卡驱动处理数据的时候,工作在中断上下文,它的处理结果就是将数据包尽快的送入此队列。

而真正的网络层、传输层协议分析和处理逻辑是在 net_rx_action 软中断中完成的。

net_rx_action CPU 调度后,从此队列中接收数据包,进行网络层和传输层的协议分析和处理。

 

 

2、BSD socket 层队列: sock-> receive_queue

每个 struct socket 对应一个这样的队列。此队列将 net_rx_action 软中断与 socket 层的处理逻辑区分开来。

 

net_rx_action 完成了网络层和传输层的处理后,需要将数据包送到对应的 struct socket 结构的此队列中。

而应用层则通过 readrecvfrom 等系统调用从此队礼中取走数据。

 

 

数据包发送路径上的队列:

 

 

1BSD socket 层队列:sock->write_queue

当从 user space 调用 write() sendto() sendmsg() 等系统调用发送数据的时候,TCP UDP 的处理过程有所不同。

 

对于 UDP 来说,在这一层不需要通过队列来缓冲数据。Sendto 或者 sendmsg  将原始数据拆分成 sk_buff 结构,然后进行传输层、网络层处理,最后送到相应的 device 对应的输出队列。

 

但是 TCP 是一种流协议,具有拥塞控制的功能,因此可能需要在这一层缓冲数据。这个队列就是struct socket 结构中的write_queue

 

为什么说可能需要用到这个队列了。因为 write 或者 sendmsg 在将原始数据拆分成 sk_buff 结构后,就尝试将这些数据包发送出去并等待对端的 ACK。如果网络足够流畅,那么就不需要将数据缓冲在队列中。如果网络比较繁忙,在设定时间内等不到 ACK,或者对端的 TCP 窗口不允许接收数据,那么就必须先缓冲到队列中,留到合适的时机再发。(显然会有一个定时器来干这个事情)。

 

2、网络设备的队列: dev->qdisc

每个网络设备对应一个此队列(也可以没有,例如 lo)。它将网络层与设备驱动区分开来。

数据包在网络层处理完之后,可能会缓冲到此队列中。

这里也只是可能会缓冲,因为数据包被送到此队列后,如果条件允许,立刻就被发送出去。

 

此队列是 Linux 内核中实现 QoS 的关键。不同的 QoS 策略采用不同的方式对此队列中的数据包进行处理,默认的方式是 FIFO

 

如果底层 driver 处理速度跟不上发送数据包的速度,那么当队列满了以后,后续的数据包就会被丢弃。

抱歉!评论已关闭.