TCP中RTT的测量和RTO的计算 以及 接收缓存大小的动态调整
RTT测量
在发送端有两种RTT的测量方法,但是因为TCP流控制是在接收端进行的,所以接收端也需要
有测量RTT的方法。
/* Receiver "autotuning" code.
*
* The algorithm for RTT estimation w/o timestamps is based on
* Dynamic Right-Sizing (DRS) by Wu Feng and Mike Fisk of LANL.
* <http://public.lanl.gov/radiant/pubs.html#DRS>
*
* More detail on this code can be found at
* <http://staff.psc.edu/jheffner/>,
* though this reference is out of date. A new paper
* is pending. 不管是没有使用时间戳选项的RTT采样,还是使用时间戳选项的RTT采样,都是获得一个RTT样本。 之后还需要对获得的RTT样本进行处理,以得到最终的RTT。
对于没有使用时间戳选项的RTT测量方法,不进行微调。因为用此种方法获得的RTT采样值已经偏高而且收敛 很慢。直接选择最小RTT样本作为最终的RTT测量值。 对于使用时间戳选项的RTT测量方法,进行微调,新样本占最终RTT的1/8,即rtt = 7/8 old + 1/8 new。 */
static void tcp_rcv_rtt_update(struct tcp_sock *tp, u32 sample, int win_dep)
{
u32 new_sample = tp->rcv_rtt_est.rtt_us;
long m = sample; if (m == 0)
m = 1;/* 时延最小为1ms*、*/ if (new_sample != 0) { /* 不是第一次获得样本*/
/* If we sample in larger samples in the non-timestamp
* case, we could grossly overestimate the RTT especially
* with chatty applications or bulk transfer apps which
* are stalled on filesystem I/O.
*
* Also, since we are only going for a minimum in the
* non-timestamp case, we do not smooth things out
* else with timestamps disabled convergence takes too
* long. /* 对RTT采样进行微调,新的RTT样本只占最终RTT的1/8 *
*/
if (!win_dep) {//需要对采样进行微调
m -= (new_sample >> 3);
new_sample += m;
} else { /* 不对RTT采样进行微调,直接取最小值,原因可见上面那段注释*/
m <<= 3;
if (m < new_sample)
new_sample = m;
}
} else {
/* No previous measure. 第一次获得样本*//注意,Linux内核为了避免浮点运算,RTT采样都是按8倍存储的*/
new_sample = m << 3;//注意,Linux内核为了避免浮点运算,RTT采样都是按8倍存储的
} tp->rcv_rtt_est.rtt_us = new_sample;/* 更新RTT*/
}
/*
此函数的原理:我们知道发送端不可能在一个RTT期间发送大于一个通告窗口的数据量。
那么接收端可以把接收一个确认窗口的数据量(rcv_wnd)所用的时间作为RTT。接收端收到一个数据段,
然后发送确认(确认号为rcv_nxt,通告窗口为rcv_wnd),开始计时,RTT就是收到序号为rcv_nxt + rcv_wnd的数据段所用的时间。
很显然,这种假设并不准确,测量所得的RTT会偏大一些。所以这种方法只有当没有采用时间戳选项时才使用,
而内核默认是采用时间戳选项的(tcp_timestamps为1)。
下面是一段对此方法的评价:
If the sender is being throttled by the network, this estimate will be valid. However, if the sending application did nothave any data to send,
the measured time could be much larger than the actual round-trip time. Thus this measurementacts only as an upper-bound on the round-trip time.
———————————————— */
static inline void tcp_rcv_rtt_measure(struct tcp_sock *tp)
{
u32 delta_us;
/* 第一次接收到数据时,需要对相关变量初始化*/ if (tp->rcv_rtt_est.time.v64 == 0)
goto new_measure;
/* 收到指定的序列号后,才能获取一个RTT测量样本*/
if (before(tp->rcv_nxt, tp->rcv_rtt_est.seq))
return;
/* RTT的样本:jiffies - tp->rcv_rtt_est.time */
delta_us = skb_mstamp_us_delta(&tp->tcp_mstamp, &tp->rcv_rtt_est.time);
tcp_rcv_rtt_update(tp, delta_us, 1); new_measure:
tp->rcv_rtt_est.seq = tp->rcv_nxt + tp->rcv_wnd;
tp->rcv_rtt_est.time = tp->tcp_mstamp;
}
/*但是在流量小的时候,通过时间戳采样得到的RTT的值会偏大,此时就会采用 没有时间戳时的RTT测量方法。*/
static inline void tcp_rcv_rtt_measure_ts(struct sock *sk,
const struct sk_buff *skb)
{
struct tcp_sock *tp = tcp_sk(sk);
if (tp->rx_opt.rcv_tsecr &&/* 启用了Timestamps选项,并且流量稳定*/
(TCP_SKB_CB(skb)->end_seq -
TCP_SKB_CB(skb)->seq >= inet_csk(sk)->icsk_ack.rcv_mss))
/* RTT = 当前时间 - 回显时间*/
tcp_rcv_rtt_update(tp,
jiffies_to_usecs(tcp_time_stamp -
tp->rx_opt.rcv_tsecr),
0);
}
调整接收缓存
数据从TCP接收缓存复制到用户空间之后,会调用tcp_rcv_space_adjust()来调整TCP接收缓存和接收窗口上限的大小
/*
* This function should be called every time data is copied to user space.
* It calculates the appropriate TCP receive buffer space.
tp->rcvq_space.space表示当前接收缓存的大小(只包括应用层数据,单位为字节)。
sk->sk_rcvbuf表示当前接收缓存的大小(包括应用层数据、TCP协议头、sk_buff和skb_shared_info结构, tcp_adv_win_scale微调,单位为字节)
*/
void tcp_rcv_space_adjust(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
int time;
int copied;
/*计算上次调整到现在的时间*/ time = skb_mstamp_us_delta(&tp->tcp_mstamp, &tp->rcvq_space.time);
/* 调整至少每隔一个RTT才进行一次,RTT的作用在这里?
//注意,Linux内核为了避免浮点运算,RTT采样都是按8倍存储的
或者没哟计算出接收方rtt?/
if (time < (tp->rcv_rtt_est.rtt_us >> 3) || tp->rcv_rtt_est.rtt_us == 0)
return; /* Number of bytes copied to user in last RTT */
/* 一个RTT内接收方应用程序接收并复制到用户空间的数据量*/
copied = tp->copied_seq - tp->rcvq_space.seq;
if (copied <= tp->rcvq_space.space) /* 如果这次的space比上次的 小*/
goto new_measure; /* A bit of theory :
* copied = bytes received in previous RTT, our base window
* To cope with packet losses, we need a 2x factor
* To cope with slow start, and sender growing its cwin by 100 %
* every RTT, we need a 4x factor, because the ACK we are sending
* now is for the next RTT, not the current one :
* <prev RTT . ><current RTT .. ><next RTT .... >
*/
/* 如果这次的space比上次的 大 */
/* 启用自动调节接收缓冲区大小,并且接收缓冲区没有上锁*/ if (sysctl_tcp_moderate_rcvbuf &&
!(sk->sk_userlocks & SOCK_RCVBUF_LOCK)) {
int rcvwin, rcvmem, rcvbuf; /* minimal window to cope with packet losses, assuming
* steady state. Add some cushion because of small variations.
*/
rcvwin = (copied << 1) + 16 * tp->advmss; /* If rate increased by 25%,
* assume slow start, rcvwin = 3 * copied
* If rate increased by 50%,
* assume sender can use 2x growth, rcvwin = 4 * copied
*/
if (copied >=
tp->rcvq_space.space + (tp->rcvq_space.space >> 2)) {
if (copied >=
tp->rcvq_space.space + (tp->rcvq_space.space >> 1))
rcvwin <<= 1;
else
rcvwin += (rcvwin >> 1);
}
/* 一个数据包耗费的总内存包括:
* 应用层数据:tp->advmss,
* 协议头:MAX_TCP_HEADER,
* sk_buff结构,
* skb_shared_info结构。
*/ rcvmem = SKB_TRUESIZE(tp->advmss + MAX_TCP_HEADER);
while (tcp_win_from_space(rcvmem) < tp->advmss)
rcvmem += 128; // 为啥微调是128
/*不能超过允许的最大接收缓冲区大小*/ rcvbuf = min(rcvwin / tp->advmss * rcvmem, sysctl_tcp_rmem[2]);
if (rcvbuf > sk->sk_rcvbuf) {
sk->sk_rcvbuf = rcvbuf;/* 调整接收缓冲区的大小*/ /* Make the window clamp follow along. */
tp->window_clamp = rcvwin;/*调整接收窗口的上限*/
}
}
tp->rcvq_space.space = copied;//更新接收方窗口上限 new_measure:
tp->rcvq_space.seq = tp->copied_seq;
tp->rcvq_space.time = tp->tcp_mstamp;
}
sk->sk_rcvbuf:分配给连接的接收使用的buf大小(size of receive buffer in bytes)。通常将数据拷贝给用户后会根据历史接收情况重新计算sk_rcvbuf(具体参见tcp_rcv_space_adjust)。
sk->sk_rmem_alloc:接收已经使用的buf大小。(接收到报文会相应增加,将数据交给用户后会相应减少)
tp->window_clamp:连接窗口的上限(Maximal window to advertise)。通常是通过历史接收情况估算出当前需要通告的最优窗口(具体参见tcp_rcv_space_adjust)。rcv_ssthresh动态调整最终会趋向于这个值。
tp->rcv_ssthresh:当前允许通告的最大窗口(Current window clamp)。窗口的选择大多时候都由rcv_ssthresh决定,而rcv_ssthresh是会动态调整的。而rcv_ssthresh调整的上限就是tp->window_clamp
TCP中RTT的测量和RTO的计算 以及 接收缓存大小的动态调整的更多相关文章
- TCP中RTT的测量和RTO的计算
https://blog.csdn.net/zhangskd/article/details/7196707 tcp传输往返时间是指:发送方发送tcp断开时, 到发送方接收到改段立即响应的所耗费的时间 ...
- TCP接收缓存大小的手动调整
给出了几个可调节的参数,它们可以帮助您提高 Linux TCP/IP 栈的性能. 表 1. TCP/IP 栈性能使用的可调节内核参数 可调节的参数 默认值 选项说明 /proc/sys/net/cor ...
- TCP系列15—重传—5、Linux中RTO的计算
之前我们介绍的都是协议中给出的RTO计算方法,下面我们看一下linux实现中RTO的计算方法.在linux中维护了srtt.mdev.mdev_max.rttvar.rtt_seq几个状态变量用来计算 ...
- 三十天学不会TCP,UDP/IP网络编程 -- TCP中的智慧之连续ARQ
突然发现上一篇文章贴图有问题,关键我怎么调也调不好,为了表达歉意,我再贴一篇gitbook上的吧,虽然违背了我自己的隔一篇在这里发一次的潜规则~其余完整版可以去gitbook(https://www. ...
- TCP中的RST复位信号
TCP中的RST复位信号 在TCP协议中RST表示复位,用来关闭异常的连接,在TCP的设计中它是不可或缺的. 发送RST包关闭连接时,不必等缓冲区的包都发出去,直接就丢弃缓存区的包发送RST包.而接收 ...
- /proc/net/tcp中各项参数说明
/proc/net/tcp中的内容由tcp4_seq_show()函数打印,该函数中有三种打印形式,我们这里这只列出状态是TCP_SEQ_STATE_LISTENING或TCP_SEQ_STATE_E ...
- TCP/IP源码(59)——TCP中的三个接收队列
http://blog.chinaunix.net/uid-23629988-id-3482647.html TCP/IP源码(59)——TCP中的三个接收队列 作者:gfree.wind@gmai ...
- 【网络协议】TCP中的四大定时器
前言 对于每个TCP连接,TCP一般要管理4个不同的定时器:重传定时器.坚持定时器.保活定时器.2MSL定时器. 重传定时器 非常明显重传定时器是用来计算TCP报文段的超时重传时间的(至于超时重传时间 ...
- TCP中的MSS解读(转)
本文摘录自TCP中的MSS解读. MSS 是TCP选项中最经常出现,也是最早出现的选项.MSS选项占4byte.MSS是每一个TCP报文段中数据字段的最大长度,注意:只是数据部分的字段,不包括TCP的 ...
随机推荐
- 关于ptype_all和pypte_base中的pt_prev的说明[转]
不知道原帖,我是从这里看到了,解决了迷惑我很久的疑问,抄过来. 看见noble_shi兄弟"关于net_rx_action函数的若干问题"贴中关于pt_prev的问题, 本来想在论 ...
- 本溪6397.7539(薇)xiaojie:本溪哪里有xiaomei
本溪哪里有小姐服务大保健[微信:6397.7539倩儿小妹[本溪叫小姐服务√o服务微信:6397.7539倩儿小妹[本溪叫小姐服务][十微信:6397.7539倩儿小妹][本溪叫小姐包夜服务][十微信 ...
- C语言中数组与指针的异同之处!你不知道的编程奥秘~
C语言的数组和指针一直是两个容易混淆的东西,当初在学习的时候,也许为了通过考试会对指针和数组的一些考点进行突击,但是很多极其细节的东西也许并不是那么清楚.本篇侧重点在于分析数组与指针的关系,什么时候数 ...
- 【C++入门学习笔记】函数和对象!你需要这一篇文章入门C++!
一.本篇要学习的内容和知识结构概览 二.知识点逐条分析 1. 混合型语言 C++源文件的文件扩展名为.cpp, 也就是c plus plus的简写, 在该文件里有且只能有一个名为main的主函数, ...
- git 撤销push到远程仓库的无用commit
一 回退代码 git reset <版本号> --soft // 软回退 - 所有的commit修改都被撤销了,且修改的代码统一撤回到暂存区 git reset <版本号> - ...
- Anderson《空气动力学基础》5th读书笔记 第5记——推导二维机翼的空气动力学系数
机翼的受力分析图 我们知道,空气对一个物体产生的升力和阻力以及力矩源于作用在整个物体上的压力分布和剪切力分布,所以我们分析上图可知(取单位展长的机翼): 对于上表面: ...
- 《我想进大厂》之JVM夺命连环10问
这是面试专题系列第五篇JVM篇. 说说JVM的内存布局? Java虚拟机主要包含几个区域: 堆:堆Java虚拟机中最大的一块内存,是线程共享的内存区域,基本上所有的对象实例数组都是在堆上分配空间.堆区 ...
- windows下安装RabbitMq和常用命令
----RabbitMq安装-----windows下安装:(1)首先windows下安装好了erlang和rabbitmq.如下地址同时下载和安装:Erlang:http://www.erlang. ...
- 使用Node.js给图片加水印的方法
一.准备工作: 首先,确保你本地已经安装好了node环境. 然后,我们进行图像编辑操作需要用到一个Node.js的库:images. 这个库的地址是:https://github.com/zhangy ...
- symfony3.4 配置高效的邮箱swiftmailer插件
网站经常需要邮件发送,symfony里面swiftmailerBundle是一个非常并且稳定的发送邮件的第三方插件. 我们来看一下symfony里面的邮件怎么配置. 第一步我们先下载bundle: c ...