本文目录导读:

net.ipv4.tcp_sack 这个内核参数本身是一个开关(通常为 0 或 1),用于启用或禁用 TCP 的选择性确认(Selective Acknowledgment, SACK)功能。
它并不是用来设置“如何选择”确认的算法细节,而是决定是否允许这种机制工作。
当 net.ipv4.tcp_sack = 1(默认值,也是推荐值)时,Linux 内核的 TCP 协议栈会自动遵循 RFC 2018 和 RFC 2883 的规范来执行 SACK,这个“如何确认”的逻辑是由内核协议栈的代码逻辑自动完成的,用户通常只需要控制开关。
下面解释一下当这个开关打开后,内核“如何选择确认”的底层逻辑:
核心目的:解决“传统确认”的低效问题
- 传统确认(累积确认): 接收方只能告诉发送方“我已经收到了所有序号小于 X 的字节”,如果中间丢了几个包(比如接收到了 1、2、4、5,但没收到 3),接收方只能不断重复确认“已收到 2”(重复 ACK),发送方就只能盲目地重传从 3 开始的所有数据(直到超时或收到 3 个重复 ACK)。
- SACK 确认: 接收方可以精确地告诉发送方:“我收到了 1-2,也收到了 4-5(但这中间有空洞)”,这样发送方就只重传丢失的包(3),而不是重传整个窗口,大大提高了带宽利用率和速度。
“如何选择”的具体机制(内核自动执行)
当 tcp_sack = 1 时,接收方的内核协议栈会:
- 检测接收队列中的空洞: 当收到乱序的 TCP 数据段时,内核会标记哪些序号是连通的(不连续区域)。
- 生成 SACK 选项块: 在返回的 ACK 包中,除了传统的
ack字段(确认已连续收到的最大序号)外,还会附加一个或多个SACK选项块,每个块描述一个连续接收到的、不连续的数据段范围([Left Edge, Right Edge])。ACK 3+SACK: 4-5(告诉对方,我期望第 3 个字节,但在我收到的乱序数据里,第 4 到第 5 字节已经在了)。
- 更新 SACK 块: 随着更多乱序数据到达,内核会动态更新 SACK 块,合并相邻的已确认范围。
- 限制块的数量: TCP 头部选项空间有限(40 字节),一个 SACK 块需要 8 字节,Linux 内核通常会在一个 ACK 中携带最多 3 到 4 个 SACK 块(这是自动协商和限制的),如果空洞太多,会优先报告最新的或最重要的空洞。
发送方如何使用这些“选择确认”(这也是自动的)
当发送方收到带有 SACK 选项的 ACK 时,它的内核(也开启了 tcp_sack=1)会:
- 更新丢失状态: 根据 SACK 报告的空洞,标记哪些数据包“丢失了”(即使还没超时)。
- 选择性重传: 只重传被 SACK 信息证实丢失的那部分数据,而不是整个窗口。
- 管道算法(如 FACK): 在 Linux 内核中,当 SACK 启用时,通常会使用
net.ipv4.tcp_fack(Forward Acknowledgment)等算法,利用 SACK 信息更精确地估算网络中正在传输的数据量,从而在不造成拥塞的前提下更快地发送新数据或重传丢失数据。
你应该如何配置 net.ipv4.tcp_sack?
-
推荐值:
1(启用)。- 它能显著提升网络在丢包情况下的性能。
- 几乎所有现代操作系统和网络设备都支持。
- 例外情况: 除非你遇到了与特定老旧防火墙或网络设备(它们错误地处理了 TCP 选项)的兼容性问题,否则永远不要关闭它。
-
如何查看:
sysctl net.ipv4.tcp_sack
-
如何永久启用(通常默认就是):
echo "net.ipv4.tcp_sack = 1" >> /etc/sysctl.conf sysctl -p
net.ipv4.tcp_sack 不是一个“如何选择”的配置参数,它只是一个开关,当你打开它:
- 接收方内核会自动在 ACK 包中包含“我已收到了哪些不连续的部分”这一确定信息(即“选择性确认”)。
- 发送方内核会自动利用这些信息,只重传真正丢失的数据包,而不是浪费带宽重传整个窗口。
你不需要自己决定“如何选择”,内核会根据 TCP 规范自动、高效地完成。 你的任务就是确保这个参数是 1。
版权声明:除非特别标注,否则均为本站原创文章,转载时请以链接形式注明文章出处。