3).参数tcp_adv_win_scale的含义
对比我上面的图示,上述代码的注释,我们知道tcp_adv_win_scale就是控制“载荷/载体”比例的,我们看一下其Kernel DOC
tcp_adv_win_scale - INTEGER Count buffering overhead as bytes/2^tcp_adv_win_scale (if tcp_adv_win_scale > 0) or bytes-bytes/2^(-tcp_adv_win_scale), if it is <= 0. Possible values are [-31, 31], inclusive. Default: 1这个参数曾经的default值是2而不是1,这意味着以往TCP的载荷占比由3/4变成了1/2,好像是开销更大了,这是为什么呢?以下是该Change的patch描述:
From: Eric Dumazet <edum...@google.com>单纯从TCP载荷比来讲,开销的增加意味着效率的降低,然而注意到这部分开销的增加并非网络协议头所为,而是skb_shared_info结构体被计入开销以及skb结构体等系统载体的膨胀所导致:
[ Upstream commit b49960a05e32121d29316cfdf653894b88ac9190 ]
tcp_adv_win_scale default value is 2, meaning we expect a good citizen
skb to have skb->len / skb->truesize ratio of 75% (3/4)
In 2.6 kernels we (mis)accounted for typical MSS=1460 frame :
1536 + 64 + 256 = 1856 'estimated truesize', and 1856 * 3/4 = 1392.
So these skbs were considered as not bloated.
With recent truesize fixes, a typical MSS=1460 frame truesize is now the
more precise :
2048 + 256 = 2304. But 2304 * 3/4 = 1728.
So these skb are not good citizen anymore, because 1460 < 1728
(GRO can escape this problem because it build skbs with a too low
truesize.)
This also means tcp advertises a too optimistic window for a given
allocated rcvspace : When receiving frames, sk_rmem_alloc can hit
sk_rcvbuf limit and we call tcp_prune_queue()/tcp_collapse() too often,
especially when application is slow to drain its receive queue or in
case of losses (netperf is fast, scp is slow). This is a major latency
source.
We should adjust the len/truesize ratio to 50% instead of 75%
This patch :
1) changes tcp_adv_win_scale default to 1 instead of 2
2) increase tcp_rmem[2] limit from 4MB to 6MB to take into account
better truesize tracking and to allow autotuning tcp receive window to
reach same value than before. Note that same amount of kernel memory is
consumed compared to 2.6 kernels.
我们分别来看一下2.6.32和3.10两个版本的sk_buff的大小,怎么看呢?不要想着写一个模块然后打印sizeof,直接用slabtop去看即可,里面信息很足 。
a).2.6.32版本的sk_buff大小
slabtop的结果是:
skbuff_head_cache 550 615 256 15 1 : tunables 120 60 8 : slabdata 41 41 0
我们看到其大小是256字节 。
b).3.10版本的sk_buff大小
slabtop的结果是:
skbuff_head_cache 3675 3675 320 25 2 : tunables 0 0 0 : slabdata 147 147 0
我们看到其大小是320字节 。
差别并不是太大!这不是主要因素,但确实会有所影响 。
除了skb的膨胀之外,系统中还有别的膨胀,比如为了效率的“对齐开销”,但更大的开销增加是skb_shared_info结构体的计入(个人认为以前开销中不计入skb_shared_info结构体是错误的)等,最终导致新版本(以3.10+为例)的内核计算TRUESIZE的方法改变:
packet_size = mss + MAX_TCP_HEADER + SKB_DATA_ALIGN(sizeof(struct sk_buff)) + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
然而以往的老内核(以2.6.32为例),其开销的计算是非常鲁莽的,少了很多东西:
packet_size = mss + MAX_TCP_HEADER + 16 + sizeof(struct sk_buff);
虽然这种开销的膨胀在TCP层面几乎看不到什么收益(反而付出了代价,你不得不配置更大的rcvbuf...),然而skb等并不单单服务于TCP,这种膨胀的收益可能被调度,中断,IP路由,负载均衡等机制获取了,记住两点即可:首先,Linux内核各个子系统是一个整体,其次,内存越来越便宜而时间一去不复返,空间换时间,划得来!
2.如何规避接收缓存溢出的风险在谈如何规避溢出风险之前,我必须先说一下这个风险并不是常在的,如果应用程序非常迅速的读取TCP数据并释放skb,那么几乎不会有什么风险,问题在于应用程序并不受TCP层的控制,所以我说的“溢出风险”指的是一种合理但很极端的情况,那就是应用程序在TCP层收满一窗数据前都不会去读取数据,这虽然很极端,但是符合TCP滑动窗口的规范:通告给发送端的窗口表示发送端可以一次性发送这么多的数据,至于应用程序什么时候来读取,滑动窗口机制并不控制 。
在阐明了风险的来源后,我们就可以畅谈何以规避风险了 。
我们知道,TCP拥塞控制通过慢启动来规避突发造成的网络缓存溢出的的风险,事实上拥塞控制也是一种流量控制,作为标准的方案,慢启动几乎是规避溢出的标配方案!这很好理解,慢启动的含义是“快速地从起点试探到稳态”,并非其字面含义所说的“慢慢地启动”,之所以有“慢”字是因为与进入稳定状态后相比,它的起点是低的 。这和开车是一样的道理,静止的汽车从踩下油门开始一直到匀速,是一个快速加速的过程,达到100km/h的时间也是一个重要的指标,当然,很多情况下是越小越好!
推荐阅读
- 运维日志分析工具ELK:Windows与Linux皆可安装
- 关于饮食养生的书 论饮食这本书
- Linux 下让工作效率翻倍的四个实用技巧
- 碧螺春,龙井,关于西湖龙井
- 翡翠|关于翡翠的纹和裂
- 有限状态机 多图详解TCP三次握手和四次挥手
- Linux主流架构运维工作简单剖析
- Linux 软链接的使用和具体演示
- 运维人员常用的 Linux 命令汇总
- linux内核调度算法--快速找到最高优先级进程
