很多研究TCP的人,总喜欢把限制放在嘴边。 我猜想有两种原因:一是QUIC的工业化,如今大把大把的人去研究UDP,当过度渲染UDP的好,自然要从鸡蛋里挑骨头,把TCP说的一文不值,限制一词再合适不过。二是很少和TCP打交道的人,在读TCP的实现时,会看到有很多地方在限制发送数据包的大小,他们会举出具体的例子,如kernel里的tcp_write_xmit 函数等。
正巧,近几日,这些声音不绝于耳,甚至好几次发声在公众场合。
于是,有了这篇文章。
恰巧整理TCP-TSQ,所以主角也定下来了。
TCP-TSQ是干什么的?TCP-TSQ的目的是控制数据包发出前排队的本地队列大小。在网卡触发TX中断以前,通过网络协议栈的数据包总会在Qdisc/device的队列排队,消耗本地缓冲资源。这会引发很多问题,如BufferBloat、RTT测量不准、公平性,相比较于拥塞算法关注“网络拥塞”问题,TCP-TSQ则是关注这种“本地拥塞”。
在源码中,TCP-TSQ也是一股子“限制”的气味,但背后的逻辑却完全看不到“限制”。
本地队列紧张,TCP-TSQ不去加重Qdisc/device队列的紧张感,但也不会傻傻地呆着不干活,它会让上层发送包在队列紧张缓解前,尽可能地将上层应用的“消息”加载到一个skb,或者是探测更大且及时的接收窗口。
当本地队列无包可发时,TSQ标识的那些阻塞数据包(大小>=原来的数据包大小)会更快地注入空闲的队列里。这种行为是TSQ通过对每个处理器定义一个独立的tasklet结构去处理的。
如此,TCP-TSQ实现了一个控制阀,可以保证每个socket发送数据包在本地队列的多少,kernel里对于新数据包,可以容忍两个skb包大小或者1ms的发送流量,并能在系统级对数据包大小设置限制(这里确实是限制。),对重传的要求放大一倍。
TCP-TSQ关注“本地拥塞”,对Qdisc/device的队列大小负责,往往会被看成发送前的最后一环。
TSQ 阻塞
TCP-TSQ tcp_small_queue_check 的阻塞依赖于sk->sk_wmem_alloc 。原因是sk->sk_wmem_alloc在发送数据包前一直存在,且变化可以及时跟踪网卡的本地队列大小。
TSQ-tasklet 阻塞包处理
TSQ单独的tasklet发送数据包逻辑,保证本地队列的有包可发。 到这里,“限制”就更不合适TCP-TSQ了。
最后,TCP-TSQ没有高的优先级,所以延迟被处理往往发生。
今天的主角是TCP-TSQ,但也想通过TSQ机制给大家灌输一种理念,TCP并不是一种限制。 原因是最近我总听到很多人闲聊,说TCP纯粹是一种束缚。我并没有被冒犯,我有时也总喷TCP怎么怎么垃圾,但看待问题时要正面,TCP也更多地在为更快的传输服务,并不能因为一个孩子长的不俊俏,就否认它的价值。
当我在为一个人,一件事辩解时,我总会处在劣势,所以现实中习惯了不解释。但想着一些问题,总有人得发声,于是我可能又做了第一个辩护人。
|