TCP 的演化史-fast retransmit/recovery

news2024/12/24 11:37:23

工作原因要对一个 newreno 实现增加 sack 支持。尝试写了 3 天 C++,同时一遍又一遍梳理 sack 标准演进。这些东西我早就了解,但涉及落地写实现,就得不断抠细节,试图写一个完备的实现。

这事有更简单的方法。根本没必要完全实现 RFC,目标是高吞吐,而不是实现标准 TCP,因此只要保证最宽容的可用性,剩下的交给现实。我们做 cc 时,为提高性能,何尝不是这里 cwnd += 10,那边 cwnd += whatever(sk),异曲同工。

前面两周写了一些关于 TCP 演进的文字,无论怎样,这是个好机会以一个实例来展示演进过程中的方法论。捕捉其中关于 sack 的两个细节,降窗和重传,再写一些文字。

TCP 丢包后进入 fast retransmit/recovery 后涉及如何降窗和如何重传两件事,TCP 经过 40 多年的演化,关于这两件事铸建了 4 个里程碑。

最初的 TCP 只有 rto,没有 fast retransmit,此即 TCP tahoe,只要有丢包,即将 cwnd = 1,重新开始慢启动。第 1 个里程碑是 TCP reno 和 TCP newreno,二者一脉相承。

reno 引入了 fast retransmit,但有很多问题,newreno 解决了这些问题。这部分参见 Wiki。seastar 只实现了 RFC6582 定义的 newreno,非常原始,但基本上这就是一个最小能用的 TCP 实现。

从第 2 个里程碑开始,TCP 开始起飞。我的工作也就是尽量追着这个尾迹跟着飞,在必要时加入一些自己的 trick,或觉得标准实现太麻烦时偷一下懒裁剪掉些东西。

引入 fast retransmit 时 TCP 还不支持 sack,收到 3 个 dupack 即进入 fast retransmit/fast recovery,但只留下一个未被 ack 的 hole,hole 后面的情况什么也看不到,没有任何启发式手段让 sender 猜测哪些 seg 丢了。

降窗如 范雅各布森 所述,直接将 cwnd = ssthresh = cwnd / 2。RFC5681 不允许超过一半的 seg 在途(这似乎是在迎合或确认 ssthresh = cwnd/2 这件事,详见:雅各布森管道):

until all lost segments in the window of data in question are repaired, the number of segments transmitted in each RTT MUST be no more than half the number of outstanding segments when the loss was detected.

这显然降低了带宽利用率,但这时没有任何信息指示如何做得更好(每发送一个 seg 只能至多带回 1 个 ack),解决这个问题的条件尚未具备。

关于重传,除了一个 hole 没有任何辅助信息,只能从 una 开始每次重传 1 个往前挪 nua,没有任何别的办法。如下图所示,整个图景相当于一个 seq space 构成的一维世界,一 hole 以障目:
在这里插入图片描述
随着 sack 被引入,降窗和重传逻辑均起了大变化。我们关注的是这个变化是如何如丝般顺滑而一气呵成的,以便这个方法论能指示 TCP(or any others) 未来的演化方向。

第 3 个里程碑来自 sack 被引入后。详见 RFC6675。 sack 反馈了更加丰富的信息,sender 有了绕到 hole 后面查看究竟的途径,这相当于引入了额外的维度,将 seq space 直立了起来,仰起头就能看到 seq space 的细节:
在这里插入图片描述
TCP 将 seq space 抽象成一个 scoreboard,该 scoreboard 上对 seq space 的每一个 seg 区分对待,可 mark sacked,mark lost,mark reordering,针对 lost seg 进行重传并 mark retrans,inflight 从而可精确获取:

inflight = snd_nxt - snd_una + snd_retrans - snd_lost - snd_sacked

基于守恒律,于是 cwnd = inflight 更加精确。

二维世界丰富的多的信息指示 sender 做出更好的决策,不再每次 retransmit 一个 seg,TCP 重传算法第一次统一处理重传 seg 以及新 seg,它们统一受 cwnd 限制:cwnd - inflight > 0。

重传逻辑将 seg 分三个优先级,mark lost 的 seg 被优先传输,因为它们最容易补 hole 而推进 una,新 seg 第二优先,它们可能带回新的 sack,推进 mark lost,背后的考虑是尽量延后 mark lost,而剩余的最后重传:
在这里插入图片描述
下面再看降窗逻辑。

重传 seg 和新 seg 在 fast retransmit/recovery 状态被统一安排,一个 seg 带回的 sack 可导致大量 mark lost,参考前面 inflight 公式,大量 seg 由于 mark lost 被清出 pipe,inflight 急剧减少而腾出大量 pipe 空间。cwnd 一定时,后果便是 burst(可 burst 一个 cwnd = ssthresh 的数据)。而 burst 会加剧网络拥塞而丢包,形成正反馈。

TCP 第一次以 ack 携带信息(sack)而非 ack 本身来计数,引入了 scoreboard 细化了 seq space,但处理降窗的方式却没有同步跟进处理这个 burst 问题。

有两个更平滑降窗的方案,第一个是 Linux TCP 的 rate-halving 算法。rate-halving 思路很简单,不再一下子将 cwnd 折半,而是每收到 2 个 ack 将 cwnd 减 1,这样收到一个 cwnd 的 ack 后,cwnd 即原来的一半了。

但彼时 cc 已模块化,丢包降窗的目标可自定义,比如 cubic 将 cwnd 降为 0.7*cwnd,而 rate-halving 写死了比例 0.5,且未考虑 sack 计数而精度不够(sack 大大提高了判断精度,rate-halving 却没有利用任何 inflight 信息)。 后来 Google 提出了 prr 算法,将 cwnd 精确平滑收敛到 ssthresh = (1 - beta)*cwnd。

思路和 rate-halving 类似,只是 prr 基于 scoreboard 精确计算 fast retransmit/recovery 期间被 ack/sack 的数据 prr_delivered 和实际发送的数据 prr_out 以获得收益。按照正常的 seg 守恒律:

cnt = prr_delivered - prr_out

结果应为 0,意思是 prr_delivered 被确认之后才能兑换等量的 prr_out 而发出,按比例缩放这个守恒律即可:

cnt = (1 - beta)*prr_delivered - prr_out

以 beta = 0.3 为例,意思是 0.7 个单位的 prr_delivered 被确认后兑换 一个单位的 prr_out,为了满足守恒律使结果为 0,prr_out 也要进行 0.7 缩放,因此必须从 cwnd 中扣除这个差异:

cwnd = inflight - |cnt|

由此在一个 rtt 内,sender 缓慢地,平滑地,等比例地将 cwnd 降到 (1-beta)*cwnd。

降窗逻辑看起来很 perfect。

让我们回看引入 sack 后的第 3 个里程碑的重传逻辑,看它有什么问题不能解决,并且该问题还必须被解决。

随着带宽资源的增加,传输协议对带宽利用率有所期待,而不再仅仅满足于保证可用性的 AIMD 算法。需实时测量的 rate-based cc 在这个背景下被设计。

与 cwnd-based cc(cwnd 配额用尽即不可再发送) 不同,实时测量考虑 delivery rate 反馈而非仅仅 ack 时钟,需要 sender 有持续数据流可发送,即使 rwnd 憋死(矢量滑动 rwnd 导致,我对此有单边解法),在收不到重传 seg 的 sack 后,要再次重传该 seg(这在某种意义上确实可以取消 rto 了,但这是后话)。

RFC6675 标准 sack 重传机制涉及两个细节,一个是 reordering 更新,一个是依赖前者的 mark lost,先看 reordering 更新:
在这里插入图片描述
reordering 根据 sack 的布局和批次不断被更新,关于 reordering 的细节,详见早期的一篇分析:TCP 的乱序&丢包判断。

基于此,再看 mark lost:
在这里插入图片描述
这两个机制揉在一起非常复杂。

RFC6675 的算法显然无法满足多次重传相同 seg,因为 sender 的 scoreboard 没有信息区分多次 mark lost 被重传的 seg,这就无法在 mark lost 和 mark reordering 之间做判断:
在这里插入图片描述
每个 seg 只能 mark lost 一次,如果重传丢了,只能 rto。

第 4 个里程碑就来了。

在 sack 引入的额外维度后,一维 seq space 展成了二维,sender 可以从额外的维度绕过 hole 看到信息量更大的 scoreboard。寻着同样的思路,再加一个维度,为传输加上时间轴,这就是 rack:
在这里插入图片描述
两根坐标轴就搞定了所有事情,不再需要额外的两个过程,虽然也依然会有由于 reordering 对 rack window 所做的调整,但相比更新 reordering 值本身的逻辑就太简单和直观了。

看一下清爽的 rack 全景:
在这里插入图片描述
sender 维护一个统一安排 mark lost 重传 seg 和新 seg 的按发送时间 fifo 队列。每一个 hole 只要在一定时间(比它后发送的都被 ack/sack,而它在此一定时间后仍是 hole)内没被 ack/sack 可以重新被 mark lost,与其是否被重传过以及重传过几次无关。每次传输都会将时间戳打入,以区别传输轮次。

就这样,rack 引入一个时间序解决了 reordering 和 mark lost 歧义的问题。

rack 靠时间驱动,即时间序,而此前的方法则是在 seq space 上根据字节序关系靠启发(且看 Linux TCP 的 update scoreboard)驱动,再往前,则连启发都没得启发了,因为根本没信息。过程的发展非常柔滑,值得再次总结。

一维世界,一个 hole 就堵住了,sack 相当于二维平面,sender 可绕到 hole 后看情况,顺着往后,用时间编码发送顺序,就是 rack,sender 在一个三维看板看到的信息更详细,从而可做出更精确合理的判断。

接下来的事情尚未发生(或者发生了一点点),但按照上面的推理逻辑,它应该会发送。

rack 就像一架引擎,只要 ack 时钟不断,在丢包时亦始终有 seg 被发送,这些 seg 将带回驱动引擎的进一步的 ack 流,该策略非常适合高速网络,发送引擎不再区分 seg 类型,与 scoreboard 解耦,rack 将代替任何基于重复 ack/sack 的启发算法 mark lost,发送引擎维持高速运转,源源不断提供数据用于实时测量。

BBR 即使用 rack 作为自己的丢包探测驱动。

随着带宽资源进一步丰富,类似 BBR 的算法未竞全功。由于 TCP 没有打包直接传输 seq space 字节流,导致无边界确认处理非常复杂,由此带来了 undo 歧义和 ack 歧义问题,不必要重传,不准确的 rtt 测量和 delivery rate/cwnd 计算都是其影响。这些复杂且不精确的处理是高带宽利用率的绊脚石。

问题根源在于,正向传输的 seg 是 seq space 的矢量字节流,而 ack/sack 指导 cwnd 更新的只有标量 account,丢失了 seg 传输顺序。换句话说,sender 发送的 seg 是按序的,而 sender 接收到的 ack/sack 却没有信息可还原对应 seg 的顺序。

按以往惯例,既然 rack 已将时间序编码到了 sender 本地,只需将该时间序同时编码到 seg 本身,这样 seg 将引导 ack 反馈更丰富的 receiver 端信息,该信息与 sender 的本地传输时间序进行比对,就什么都有了。过年期间,我曾想了一个方法,详见:重新设计 TCP。下面是对该文字的一点前置分析。

如果 seg 携带 1 bit 额外信息,就能区分一次重传和原始 seg,携带 2 bit 能区分 3 次重传和原始 seg,以此类推,携带 32 bit 能区分 4G 次传输(1 次原始 seg 和 4G-1 次重传),为将每次传输识别为一个不允许切割的整体,需将 seq space 的字节流按序打包进固定长度的 packet,以 packet 为单位传输并针对 packet 确认,为每一个 packet 打标递增的 packet id 作为序列号。

这想法是个自然而然的过程。逻辑反而更简单明,加入一个 packet id 便清除了启发式判断。

可 TCP 没地方编码 packet id 了,况且 TCP 的标准处理流程无法要求不切割,打包过程很难合并入 TCP。但 QUIC 接力了。虽然 QUIC 未必是 TCP 后继,但它在 sack,rack,重传等方面的处理方式,正是针对 TCP 的 bugfix,整个过程一脉相承,可理解。

后面的路怎么走我不知道,也不预测,但俯拾皆是的肯定依然一地鸡毛,而且如果它确实发生了,将它与前面的事情连起来,必将在同一条线上。

浙江温州皮鞋湿,下雨进水不会胖。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/354287.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

大型信息系统

一、大型信息系统二、信息系统的规划方法三、信息系统的规划工具 一、大型信息系统 信息系统规划(也称为信息系统战略规划)是一个组织有关信息系统建设与应用的全局性谋划,主要包括战略目标、策略和部署能内容。 信息化规划是企业信息化建设…

安全—08day

ApabilitiesapabilitiesLinux Capabilities线程的 capabilitiesPermitted 允许Effective 有效InheritableBoundingAmbient文件的 capabilitiesPermittedInheritableEffective运行 execve() 后 capabilities 的变化案例分析方法一、依次执行如下命令方法二、iptables端口转发方案…

SAP ABAP GUI_DOWNLOAD中下载乱码的问题

1 GUI_DOWNLOAD 1.1 问题表现 GUI_DOWNLOAD在应用当中有时会导致输出的文件在某些电脑正常显示,在某些电脑乱码显示。这个固然是由于各个电脑系统配置有差异,但是我们可以在应用该函数时就排除该差异来保证任意台电脑正常显示输出的文件。 如下…

英语基础-定语从句的特殊用法及写作应用

1. 定语从句的引导词省略的情况 1. that 引导定语从句,从句中缺宾语/表语,that可省略; This is the book that he likes. I like the shirt that you gave me. We do not agree on the plan that you make. China is not the country th…

论文浅尝 | SpCQL: 一个自然语言转换Cypher的语义解析数据集

笔记整理:郭爱博,国防科技大学博士论文发表会议:The 31th ACM International Conference on Information and Knowledge Management,CIKM 2022动机随着社交、电子商务、金融等行业的快速发展,现实世界编织出一张庞大而…

测试人员为什么也要学习Linux操作系统

我相信能够看到这篇文章的你,一定是对计算机感兴趣、想要增加技能从而为以后加薪打基础。今天,我就和大家谈谈我对为什么要学习 Linux 系统的看法。我将从如下这三个方面谈我的看法。 巩固基础知识 做一个合格的软件工程师 学以致用 1. 巩固基础知识 …

2023年美国大学生数学建模C题:预测Wordle结果建模详解+模型代码

目录 前言 一、题目理解 背景 解析 字段含义: 建模要求 二、建模思路 灰色预测: ​编辑 二次指数平滑法: person相关性 只希望各位以后遇到建模比赛可以艾特认识一下我,我可以提供免费的思路和部分源码,以后…

字符设备驱动基础(一)

目录 一、Linux内核对设备的分类 linux的文件种类: Linux内核按驱动程序实现模型框架的不同,将设备分为三类: 总体框架图: 二、设备号------内核中同类设备的区分 三、申请和注销设备号 四、函数指针复习 4.1、 内存四区 …

ACM数论 裴蜀定理(贝祖定理)

一.内容定义 「裴蜀定理」,又称贝祖定理(Bzouts lemma)。是一个关于最大公约数的定理。其内容定义为:对于不全为零的任意整数 a 和 b,记二者的最大公约数为 g 即 gcd(a,b) g,则对于任意整数 x 和 y 都一定…

ASEMI高压MOS管4N65SE,4N65SE参数,4N65SE特征

编辑-Z ASEMI高压MOS管4N65SE参数: 型号:4N65SE 漏极-源极电压(VDS):650V 栅源电压(VGS):30V 漏极电流(ID):4A 功耗(PD&#xf…

分析内核自带的LCD驱动程序_基于IMX6ULL

分析内核自带的LCD驱动程序_基于IMX6ULL 文章目录分析内核自带的LCD驱动程序_基于IMX6ULL参考资料:一、驱动程序框架1.1 入口函数注册platform_driver1.2 设备树有对应节点1.3 probe函数分析二、 编写硬件相关的代码2.1 GPIO设置2.2 时钟设置2.3 LCD控制器的配置致谢…

[软件工程导论(第六版)]第1章 软件工程学概述(课后习题详解)

文章目录1. 什么是软件危机?它有哪些典型表现?为什么会出现软件危机?2. 假设自己是一家软件公司的总工程师,当把图1.1给手下的软件工程师们观看,告诉他们及早发现并改正错误的重要性时,有人不同意这个观点&…

C#按边框切检验仪器图

最近碰到一个检验设备是生成PDF文件报告的。imedicallis监听程序把PDF解析出来之后发现PDF里面图不是多个小图,而是一张大图。但用户又要传到检验系统的是小图,而且小图位置和数量不固定,也不能用固定位置截取实现。为此开启一段“高端设备局…

Linux生产者消费模型

1.生产者消费者模型 1.1 为何要使用生产者消费者模型 生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接…

【淄博正大光明】收藏|三分钟带你全面了解这个神奇的镜片

对于孩子的东西 家长总是谨慎再谨慎 而对于夜间戴在眼睛里的镜片 家长更是存在很多顾虑 安全吗?有效吗? 影响孩子睡觉吗? 别着急淄博正大光明眼科医院 带你深度了解角膜塑形镜 01 角膜塑形镜究竟是什么? 角膜塑形镜是一种使用高分…

一文搞懂Linux内核进程CPU调度基本原理

为什么需要调度 进程调度的概念比较简单,我们假设在一个单核处理器的系统中,同一时刻只有一个进程可以拥有处理器资源,那么其他的进程只能在就绪队列中等待,等到处理器空闲之后才有计划获得处理器资源来运行。在这种场景下&#…

k8s快速入门

文章目录一、Kubernetes(K8S)简介1、概念1.1 Kubernetes (K8S) 是什么1.2 核心特性1.3 部署方案2、Kubernetes 集群架构2.1 架构2.2 重要概念 Pod2.3 Kubernetes 组件二、Kubernetes集群安装1、安装方式介绍2、minikubute安装3、裸机搭建(Bar…

python实用脚本(六)—— pandas库的使用(生成、读取表格)

本期主题: python的pandas使用 往期链接: python实用脚本(一)—— 批量修改目标文件夹下的文件名python实用脚本(二)—— 使用xlrd读取excelpython实用脚本(三)—— 通过有道智云AP…

Linux 日志查找常用命令

1.1 cat、zcat cat -n app.log | grep "error":查询日志中含有某个关键字error的信息,显示行号。 cat -n app.log | grep "error" --color:查询日志中含有某个关键字error的信息,显示行号,带颜色…

基于Detectron2模型和深度学习方法的改进森林火灾检测方法

1.文章信息本次介绍的文章是来自韩国科研团队的一篇2023年火灾检测文章,文章立足于森林火灾检测,题目为《An Improved Forest Fire Detection Method Based on the Detectron2 Model and a Deep Learning Approach》。2.摘要随着全球变暖和人口的增加&am…