TCP连接接收窗口更新机制

个人理解,欢迎指正
*参考linux内核源码4.1

基本原理

接收缓存大小的动态调整

接收端要想不成为瓶颈,需要提供的窗口大小win_size>=RTT*speed。linux内核通过上一个RTT的接收情况来估算当前RTT需要的窗口大小(window_clamp)。再通过窗口大小计算出所需的接收缓存大小。在连接将数据拷贝给用户时触发接收存储动态调整。具体详见tcp_rcv_space_adjust。

接收窗口大小的动态调整

一般初始化一个较小的窗口,然后逐渐增加,避免多连接的情况下大家一上来都通告大窗口导致链路拥塞。接收窗口的调整主要受限rcv_ssthresh。在连接启动阶段,rcv_ssthresh会随着连接收包不断的扩大,当rcv_ssthresh增大到一定程度后受限window_clamp,不再扩大。另外当系统内存吃紧的情况下连接会主动缩小rcv_ssthresh,避免连接占用过多内存。

相关变量解释

因为系统的内存有限,为了限制单连接占用过多的内存,连接的发送和接收buf都会有上限。接收方向由sk->sk_rcvbuf决定。连接发包时都会重新计算通告窗口,窗口的选择主要与如下几个变量相关:
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_grow_window)。

源码分析

接收缓存大小的动态调整

接收缓存大小的动态调整主要涉及的函数有
1)tcp_rcv_space_adjust():根据上一个RTT的接收情况来估算当前RTT需要的窗口和相应的接收缓存大小。连接将数据拷贝给用户后调用
2)tcp_rcv_rtt_measure():连接不开启时间戳时的RTT估算
3)tcp_rcv_rtt_measure_ts():连接开启时间戳时的RTT估算

具体函数实现:

/*
 * This function should be called every time data is copied to user space.
 * It calculates the appropriate TCP receive buffer space.
 */
void tcp_rcv_space_adjust(struct sock *sk)
{
   
	struct tcp_sock *tp = tcp_sk(sk);
	int time;
	int copied;

	time = tcp_time_stamp - tp->rcvq_space.time;
	if (time < (tp->rcv_rtt_est.rtt >> 3) || tp->rcv_rtt_est.rtt == 0)
		return;

	/* Number of bytes copied to user in last RTT */
	copied = tp->copied_seq - tp->rcvq_space.seq;
	if (copied <= tp->rcvq_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 .... >
	 */

	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->
  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值