《Linux高性能服务器编程》——3.10 拥塞控制

简介: 本节书摘来自华章计算机《Linux高性能服务器编程》一书中的第3章,第3.10节,作者 游双,更多章节内容可以访问云栖社区“华章计算机”公众号查看。

3.10 拥塞控制

3.10.1 拥塞控制概述

TCP模块还有一个重要的任务,就是提高网络利用率,降低丢包率,并保证网络资源对每条数据流的公平性。这就是所谓的拥塞控制。

TCP拥塞控制的标准文档是RFC 5681,其中详细介绍了拥塞控制的四个部分:慢启动(slow start)、拥塞避免(congestion avoidance)、快速重传(fast retransmit)和快速恢复(fast recovery)。拥塞控制算法在Linux下有多种实现,比如reno算法、vegas算法和cubic算法等。它们或者部分或者全部实现了上述四个部分。/proc/sys/net/ipv4/tcp_congestion_control文件指示机器当前所使用的拥塞控制算法。

拥塞控制的最终受控变量是发送端向网络一次连续写入(收到其中第一个数据的确认之前)的数据量,我们称为SWND(Send Window,发送窗口)。不过,发送端最终以TCP报文段来发送数据,所以SWND限定了发送端能连续发送的TCP报文段数量。这些TCP报文段的最大长度(仅指数据部分)称为SMSS(Sender Maximum Segment Size,发送者最大段大小),其值一般等于MSS。

发送端需要合理地选择SWND的大小。如果SWND太小,会引起明显的网络延迟;反之,如果SWND太大,则容易导致网络拥塞。前文提到,接收方可通过其接收通告窗口(RWND)来控制发送端的SWND。但这显然不够,所以发送端引入了一个称为拥塞窗口(Congestion Window,CWND)的状态变量。实际的SWND值是RWND和CWND中的较小者。图3-11显示了拥塞控制的输入和输出(可见,它是一个闭环反馈控制)。

image

3.10.2 慢启动和拥塞避免

TCP连接建立好之后,CWND将被设置成初始值IW(Initial Window),其大小为2~4个SMSS。但新的Linux内核提高了该初始值,以减小传输滞后。此时发送端最多能发送IW字节的数据。此后发送端每收到接收端的一个确认,其CWND就按照式(3-1)增加:

CWND+=min(N,SMSS)            (3-1)

其中N是此次确认中包含的之前未被确认的字节数。这样一来,CWND将按照指数形式扩大,这就是所谓的慢启动。慢启动算法的理由是,TCP模块刚开始发送数据时并不知道网络的实际情况,需要用一种试探的方式平滑地增加CWND的大小。

但是如果不施加其他手段,慢启动必然使得CWND很快膨胀(可见慢启动其实不慢)并最终导致网络拥塞。因此TCP拥塞控制中定义了另一个重要的状态变量:慢启动门限(slow start threshold size,ssthresh)。当CWND的大小超过该值时,TCP拥塞控制将进入拥塞避免阶段。

拥塞避免算法使得CWND按照线性方式增加,从而减缓其扩大。RFC 5681中提到了如下两种实现方式:

image

CWND+=SMSS*SMSS/CWND            (3-2)

图3-12粗略地描述了慢启动和拥塞避免发生的时机和区别。该图中,我们以SMSS为单位来显示CWND(实际上它是以字节为单位的),以次数为单位来显示RTT,这只是为了方便讨论问题。此外,我们假设当前的ssthresh是16SMSS大小(当然,实际的ssthresh显然远不止这么大)。

image

以上我们讨论了发送端在未检测到拥塞时所采用的积极避免拥塞的方法。接下来介绍拥塞发生时(可能发生在慢启动阶段或者拥塞避免阶段)拥塞控制的行为。不过我们先要搞清楚发送端是如何判断拥塞已经发生的。发送端判断拥塞发生的依据有如下两个:

image

拥塞控制对这两种情况有不同的处理方式。对第一种情况仍然使用慢启动和拥塞避免。对第二种情况则使用快速重传和快速恢复(如果是真的发生拥塞的话),这种情况将在后面讨论。注意,第二种情况如果发生在重传定时器溢出之后,则也被拥塞控制当成第一种情况来对待。

如果发送端检测到拥塞发生是由于传输超时,即上述第一种情况,那么它将执行重传并做如下调整:

ssthresh=max(FlightSize/2,2*SMSS)          (3-3)
CWMD<=SMSS

其中FlightSize是已经发送但未收到确认的字节数。这样调整之后,CWMD将小于SMSS,那么也必然小于新的慢启动门限值ssthresh(因为根据式(3-3),它一定不小于SMSS的2倍),故而拥塞控制再次进入慢启动阶段。

3.10.3 快速重传和快速恢复

在很多情况下,发送端都可能接收到重复的确认报文段,比如TCP报文段丢失,或者接收端收到乱序TCP报文段并重排之等。拥塞控制算法需要判断当收到重复的确认报文段时,网络是否真的发生了拥塞,或者说TCP报文段是否真的丢失了。具体做法是:发送端如果连续收到3个重复的确认报文段,就认为是拥塞发生了。然后它启用快速重传和快速恢复算法来处理拥塞,过程如下:

1)当收到第3个重复的确认报文段时,按照式(3-3)计算ssthresh,然后立即重传丢失的报文段,并按照式(3-4)设置CWND。

CWND=ssthresh+3*SMSS             (3-4)

2)每次收到1个重复的确认时,设置CWND=CWND+SMSS。此时发送端可以发送新的TCP报文段(如果新的CWND允许的话)。

3)当收到新数据的确认时,设置CWND=ssthresh(ssthresh是新的慢启动门限值,由第一步计算得到)。

快速重传和快速恢复完成之后,拥塞控制将恢复到拥塞避免阶段,这一点由第3步操作可得知。

相关文章
|
4月前
|
消息中间件 存储 缓存
【嵌入式软件工程师面经】Linux系统编程(线程进程)
【嵌入式软件工程师面经】Linux系统编程(线程进程)
117 1
|
25天前
|
Shell Linux
Linux shell编程学习笔记30:打造彩色的选项菜单
Linux shell编程学习笔记30:打造彩色的选项菜单
|
25天前
|
Shell Linux
Linux shell编程学习笔记82:w命令——一览无余
Linux shell编程学习笔记82:w命令——一览无余
|
1月前
|
Linux Shell
Linux系统编程:掌握popen函数的使用
记得在使用完 `popen`打开的流后,总是使用 `pclose`来正确关闭它,并回收资源。这种做法符合良好的编程习惯,有助于保持程序的健壮性和稳定性。
52 6
|
1月前
|
Linux Shell
Linux系统编程:掌握popen函数的使用
记得在使用完 `popen`打开的流后,总是使用 `pclose`来正确关闭它,并回收资源。这种做法符合良好的编程习惯,有助于保持程序的健壮性和稳定性。
89 3
|
29天前
|
Shell Linux Python
python执行linux系统命令的几种方法(python3经典编程案例)
文章介绍了多种使用Python执行Linux系统命令的方法,包括使用os模块的不同函数以及subprocess模块来调用shell命令并处理其输出。
18 0
|
2月前
|
项目管理 敏捷开发 开发框架
敏捷与瀑布的对决:解析Xamarin项目管理中如何运用敏捷方法提升开发效率并应对市场变化
【8月更文挑战第31天】在数字化时代,项目管理对软件开发至关重要,尤其是在跨平台框架 Xamarin 中。本文《Xamarin 项目管理:敏捷方法的应用》通过对比传统瀑布方法与敏捷方法,揭示敏捷在 Xamarin 项目中的优势。瀑布方法按线性顺序推进,适用于需求固定的小型项目;而敏捷方法如 Scrum 则强调迭代和增量开发,更适合需求多变、竞争激烈的环境。通过详细分析两种方法在 Xamarin 项目中的实际应用,本文展示了敏捷方法如何提高灵活性、适应性和开发效率,使其成为 Xamarin 项目成功的利器。
46 1
|
2月前
|
安全 Linux 开发工具
探索Linux操作系统:从命令行到脚本编程
【8月更文挑战第31天】在这篇文章中,我们将一起潜入Linux操作系统的海洋,从最基础的命令行操作开始,逐步深入到编写实用的脚本。无论你是初学者还是有一定经验的开发者,这篇文章都将为你提供新的视角和实用技能。我们将通过实际代码示例,展示如何在日常工作中利用Linux的强大功能来简化任务和提高效率。准备好了吗?让我们一起开启这段旅程,探索Linux的奥秘吧!
|
2月前
|
Linux
揭秘Linux心脏:那些让你的编程事半功倍的主要系统调用
【8月更文挑战第31天】Linux中的系统调用是操作系统提供给应用程序的接口,用于请求内核服务,如文件操作、进程控制等。本文列举了22种主要系统调用,包括fork()、exec()、exit()、wait()、open()、close()、read()、write()等,并通过示例代码展示了如何使用fork()创建新进程及使用open()、write()、close()操作文件。这些系统调用是Linux中最基本的接口,帮助应用程序与内核交互。
38 1
|
2月前
|
Linux 程序员 开发者
源社区的兴起:从“代码隐士”到Linux引领的“全球编程嘉年华”
在编程的古老森林中,曾有“代码隐士”默默耕耘,惧怕智慧外泄。直到“开源”春风拂过,源社区如全球编程嘉年华盛开!开源文化颠覆了“独门秘籍”的传统,像“武林秘籍共享”般在网络上公开,鼓励知识传播与智慧碰撞。程序员组队开发,分享代码,提升科技实力。Linux则从“首席大厨”变身为“总导演”,以强大内核调制出诱人应用,引领潮流并推动技术创新。加入这场没有血腥厮杀,只有知识盛宴的“编程版《饥饿游戏》”吧!与全球开发者共享编程的乐趣与成就感!别忘了带上你的“独门秘籍”,可能下一个改变世界的创意就在其中!
64 7