TCP拥塞控制图解 不包括RTO,因为它太简单了 【勘误1】

               

熬过了几个夜晚,终于把TCP的拥塞处理的Linux撸了一遍,仓促中也总结了一幅巨大的图,然而今天下午的例会讨论后,我自己说着说着发现还有一些值得商榷的地方,有的是笔误,也有的是一些细节依然没有搞清楚,因此特此勘误,原文我只修改了文字,因为重新贴图代价实在太大,再者,我希望留下一些错误的印记,这样也能看清楚整个发展的历程,希望两篇一起看。

虽然在大师级的神看来,这不算什么,但是对于我,这是一个比较浩大的工程,错误之处在所难免,请谅解!谢谢!如果能共同进步,这就是我分享的最终目的和动力。


PS:
 1.今天是我的生日,我觉得就这么睡去不符合这个特殊日子的风格,因此就趁着二两酒把这篇勘误补齐吧。
 2.这是第一篇勘误,我相信原图还有很多问题,以后会持续更新,风格和方式与这个一致。

 问题1:mark lost的时候到底是怎么进行的

 这个涉及到了原图中的What to retransmit,到底Linux内核函数tcp_mark_head_lost是怎么进行的呢?看下面的发送序列:
 UNA|hole1|hole2|sack1|sack2|hole3|hole4|sack3-7|unack1|unack4|snd_nxt|...
 我们得知,sack_out的值是2+5=7,假设默认reordering是3,那么在不启用fack的情况下,应该有7-3=4个数据包被标记为LOST,该标记哪些呢?

 选择:从una开始,标记没有被sack的4个数据包,即UNA,hole1,hole2,hole3。这也是我在原图中的理解,但事实上呢?我们开看代码(Linix 3.10),tcp_mark_head_lost:

static void tcp_mark_head_lost(struct sock *sk, int packets, int mark_head){    struct tcp_sock *tp = tcp_sk(sk);    struct sk_buff *skb;    int cnt, oldcnt;    int err;    unsigned int mss;    /* Use SACK to deduce losses of new sequences sent during recovery */    const u32 loss_high = tcp_is_sack(tp) ?  tp->snd_nxt : tp->high_seq;    ...    tcp_for_write_queue_from(skb, sk) {        if (skb == tcp_send_head(sk))            break;        /* TODO: do this better */        /* this is not the most efficient way to do this... */        tp->lost_skb_hint = skb;        tp->lost_cnt_hint = cnt;        if (after(TCP_SKB_CB(skb)->end_seq, loss_high))            break;        oldcnt = cnt;        if (tcp_is_fack(tp) || tcp_is_reno(tp) ||            // 请注意,被标记为SACKED的数据包也会占据mark lost计数!!如上例,当UNA,hole1,hole2被标记完            // 以后,下一个本应该标记hole3,但是由于sack1占据可一个“位置”,因此如果只标记4个LOST,那么将会            // 到sack1为止!!,不会再标记hole3!            // 但这并不意味着LOST标记仅仅会标记左边数第一系列的空洞,如果标记数为5,那么由于sack2也占据了一个            // 位置,标记为LOST的依然是UNA,hole1,hole2,但是如果是标记6个LOST,那么结果将是标记UNA,hole1,hole2,            // hole3为LOST!明白了吗?            (TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_ACKED))            cnt += tcp_skb_pcount(skb);        if (cnt > packets) {            ...        }        tcp_skb_mark_lost(tp, skb);        if (mark_head)            break;    }    tcp_verify_left_out(tp);}

总结:标记LOST的时候,会从左到右,标记的时候并不会跳过已经被SACK的数据包,只是,如果它已经被SACK了,那么将不会被置LOST标志。这有什么意义?在SACK reneging的时候,将会有意义。
这意味着,可以重传多少数据包和空洞的位置有关,一个合理的假设是,如果中间大量连续积累了SACKed的数据包,仅仅说明在这些数据包前面的空洞丢失的可能性比较大,这是真丢包,而不是乱序,然而话如果反着说,如果被SACKed的数据包间隔交替到来,这恰恰说明乱序的可能大于丢包,此时我们到底要重传哪些呢?如果给我安放一个偏置电阻的权力,我就可以完美解决这个问题,然而电阻可以在电子市场一块钱抓一把,跟老板混熟了还可以白拿,偏置算法可就是不是随便百度一下就能用的了....(百度?嗯,百度!)

问题2:关于When to retransmit

原图中,在描述SACK模式的时候,我在图中只画了3个数据包被SACK,但是事实上,要想触发重传,必须有至少4个数据包被SACK,这是一个笔误。

问题3:原图中的乱序检测,在描述重复SACK的时候,我将reordering的右沿滑到了snd_high,但事实上,应该是最高被SACK的那个位置,正如下面的文字描述的那样,犯这样的错误,真不应该!

问题4:严格说,这是我自己想的一个问题,原图原文不一定错,然而人不就是不断否定自己才能进步么?

请看How to retransmit,旁白中有关于RFC3517的描述,规范了传输的优先级,第一优先传输LOST数据包,第二传输新数据,然而我们看到,tcp_xmit_retransmit_queue,发现Linux实现第二优先传输新数据的时候,完全依赖“output路径”,也就是说在传输完LOST数据包之后,只要发现有新数据等待传输,直接就退出了tcp_xmit_retransmit_queue,至于说output路径到底什么时候被触发,
已经可以传输多少新数据,完全不管!
       在解耦合方面,这么做是恰当的(请注意这个函数的名字是retranmit,而不是transmit),但是难道这样不会造成窗口浪费吗?试想当前窗口为100,我有10个LOST数据需要重传,这意味着
       可以发送90个新数据包,然而可能此时没有那么多新数据包,难道这个时候进行“前向重传”不好吗?

           

再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值