包括 Linux kernel 在内的各种 TCP 实现均使用类似 skb 的对象管理一个个 packet,使 TCP 失去了 “流” 特征。应用通过 syscall 每写入一批数据,协议栈都可能生成一个 skb:
仅管理这些 skb 就是一笔大开销。除了 skb 数据结构本身的 crud 操作外,在处理 SACK 时还要涉及 split/merge,非常麻烦。为了提高效率,rtx 从链表组织成了红黑树,同时,为了记录 RACK,还有一个 时间序链表,这些链表本身都没有错,问题在于在这个框架中,skb 为主,skb->data 被挟裹在 skb 之中,只是其一个属性,本末倒置。
举一个例子,如果 SACK 携带了 12345~22345 这个 block,如何找到它?显然需要操作 skb 队列,看一下 tcp_sacktag_write_queue 这个函数就知道多复杂了。带宽越大,rtx queue 越大,问题越大越复杂。
这件事的局面如下图所示:
其实把 skb 和 data 的关系颠倒一下,将 skb 置于更下层,只负责发送之用。以 data 为主,置于一段平坦的内存空间(一般表示为 ringbuffer),用 “页表项” 管理每一个固定的小段(比如 4KB),每段都可以指向一个或几个 skb,而每一个 skb 则反过来指示自己的 data 范围,这就非常简单了。
仍然以 SACK block:12345~22345 为例,连续寻址直接定位到 12345 - una~22345 - una,删除这段区间小段所指向的 skb,对应 skb 收缩或被回收。
这件事的局面如下图所示:
就像操作系统管理 page 使用的页表一样,内存就是内存,永远在那里,而 page 对象描述其属性,TCP 数据流的平坦空间也用一个 “页表” 管理起来,连续的流永远在那里,而 skb 对象描述其属性。
但这也只是说说,Linux TCP 的实现已经摆在那里 20 年了,作为通用系统不可能改变。如果非要实现高效传输,只能指望专门的协议栈实现。
拉肚子刷头条看到一幅图,觉得不错:
在下班的路上写下一句感慨:这就是现如今各种在高速网络上运行的传输协议自找的麻烦,明明修座立交就能搞定的事,偏偏都在秀复杂的算法。
我本以为 seastar tcp 会好一些,可是依然很难用加减乘除来进行 crud。文章来源:https://www.toymoban.com/news/detail-447251.html
浙江温州皮鞋湿,下雨进水不会胖。文章来源地址https://www.toymoban.com/news/detail-447251.html
到了这里,关于TCP 协议的低效实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!