Tcp的处理中使用了三个队列,receive_queue,backlog_queue,pre_queue,在数据包到达tcp协议栈时,持有sk自旋锁,
然后检查当前使用有进程上下文操作sk的逻辑,通过sock_owned_by_user判断,如果sk_lock.owned被赋值说明进程持有sk,
如果为0则可以在当前软中断上下文中,继续数据报文的处理。
当sk_lock.owned赋值时,skb通过sk_add_backlog被放在了sk_backlog队列的尾部,等待进程release_sock时处理,在
release_sock时,会将sk_backlog临时保存(私有化,与sock脱离),然后逐一进行skb的处理。这个过程是调用sk_backlog_rcv
实现的,实际上是tcp_v4_do_rcv函数。这里的函数使用是在进程上下文中(关了bh),每处理结束一个skb后,都会检查是
否需要调度,如果需要调度,则打开bh,调度,等到重新执行时关闭bh,因此整个过程中可能会产生睡眠。
当sk_lock.owned没有赋值时,说明当前没有进程/用户在操作sk,会首先调用tcp_prequeue尝试将数据报文加入pre_queue,
如果无法加入pre_queue时,则走普通流程在软中断上下文中调用tcp_v4_do_rcv进行后续处理,后续动作与进程/用户在
release_sock中执行的sk_backlog_rcv(tcp_v4_do_rcv)相同。
tcp_prequeue函数负责向pre_queue中添加skb,无法添加的原因只有两个:
一是用户为了降低延迟,全局关闭了pre_queue的功能(通过sysctl_tcp_low_latency开关)
二是当前没有进程/用户等待处理该sk上的报文,即tp->ucopy.task为0时,也不能使用pre_queue。
下面这篇文章对prequeue做出了评价,可以参考一下,,其中对一些概念的澄清,摘录如下
tcp中分为快速路径(fast path)和慢速路径(slow path),让我想到了网络设备的control path和data path(其实不挨着),
所谓fast path不是指pre_queue path,slow path也不是指处理backlog_queue和receive_queue的path。fast path需要一些附加条件,
其含义是复制到用户空间或者按序进入receive_queue的处理路径,slow path指的是乱序报文的处理以及其他不符合fast path的数据
包的处理方式。这些path与数据报文进入那个队列没有关系,一般是将fast path分为两类:
直接复制进用户空间的路径是most-fast-path,没有进程上下文导致按序的skb进入receive_queue的是more-fast-path。
由于处理乱序包或缓冲区用尽这些情况很麻烦,因此处理这些情况的path就是slow-path,具体是什么path是根据tcp语义和数据包
特性来确定的,和具体协议栈的实现没有关系。
阅读(7002) | 评论(0) | 转发(9) |