tcp中RTO的計算以及linux下的實現


原創文章,轉載請注明: 轉載自pagefault

本文鏈接地址: tcp中RTO的計算以及linux下的實現

計算RTT以及RTO的代碼比較簡單,我們先來看原理,首先相關的rfc有兩篇分別是rfc793以及rfc6298,而相關的paper有一篇,那就是Van Jacobson和Michael J. Karels的 Congestion Avoidance and Control這篇paper,這篇1988年的paper中描述的RTT計算方法,就是我們當前所使用的計算方法,可能有的操作系統有一點修改,不過基本的東西都一樣。

首先RTT是什么,RTT簡單來說,就是我發送一個數據包,然后對端回一個ack,那么當我接到ack之后,就能計算出從我發送出包到接到過了多久,這個時間就是RTT。RTT的計算是很簡單的,就是一個時間差。

而RTO呢,RTO也就是tcp在發送一個數據包之后,會啟動一個重傳定時器,而RTO就是這個定時器的重傳時間,那么這個時候,就有問題了,由於RTO是指的這次發送當前數據包所預估超時時間,那么RTO就需要一個很好的統計方法,來更好的預測這次的超時時間。

我們所能想到的最簡單的方法,那就是取平均數,比如第一次RTT是500毫秒,第二次是800毫秒,那么第三次發送的時候,RTO就應該是650毫秒。其實經典的RTO計算方法和取平均有點類似,只不過因子不太一樣,取平均的話,也就是老的RTO和新的RTT都是占50%的權重,而在經典的RTO計算中就有些變化了。

來看經典的RTT計算方法,這個計算方法是在RFC793中提出的,計算方法是這樣子的:

1
2
SRTT = ( ALPHA * SRTT ) + ((1-ALPHA) * RTT)
   RTO = min[UBOUND,max[LBOUND,(BETA*SRTT)]]

其中ALPHA是一個scala因子,一般來說建議ALPHA是0.8和0.9.UBOUND就是RTO的最大值,LBOUND是RTO的最小值,BETA也是一個因子,建議是1.3-2.0。

這里可以看到在經典的RTO計算方法中會有一個很大的問題,就是當RTT對RTO的影響太小了,也就是說經典的RTO計算方法在RTT變化比較大的網絡中,會表現的非常不好的。

於是在1988年,大神Van jacobson和Michael J. Karels的 Congestion Avoidance and Control這篇paper中,描述了一種新的計算方法,這里對於rtt的采樣,多添加了一個因素,那就是均差(mean deviation),而這里並不是傳統意義上的均差,使用的平均數其實就是我們在經典方法中的srtt。 計算方法是這樣子的:

1
2
3
Err ≡ m−a
a←a+gErr
v ← v+g(|Err|−v)

其中m就是當前最新的RTT,然后 a就是經典方法中的的SRTT,然后v就是均差,而Err顧名思義,就是我們預測的RTT的錯誤偏差。
可是這里有個問題,那就是由於因子g的存在,那么就有可能出現浮點數,因此我們只需要做個變化,讓g=1/2^n,那么上面的的等式就變為下面這個了

1
2
2^na ← 2^na+Err
2^nv ← 2^nv+(|Err|−v)

假設g=1/8,也就是n為3,並且sa為2^n*a,sv為2^n*v,那么對應的c偽碼就如下,:

1
2
3
4
5
6
7
8
9
/∗ update Average estimator ∗/
m −= (sa >> 3);
sa += m;
/∗ update Deviation estimator ∗/
if (m < 0)
    m = −m;
m −= (sv >> 2);
sv += m;
rto = (sa >> 3) + sv;

然后我們再來看rfc6298中對RTO計算的描述,這個rfc基本上就和Van jacobson和Michael J. Karels所描述的計算方法一致。其中第一次RTO的計算方法是這樣子的:

1
2
3
SRTT <- R
RTTVAR <- R/2
RTO <- SRTT + max (G, K*RTTVAR)

后續的RTO計算是這樣子的:

1
2
3
RTTVAR <- (1 - beta) * RTTVAR + beta * |SRTT - R'|
SRTT <- (1 - alpha) * SRTT + alpha * R' 
RTO <- SRTT + max (G, 4*RTTVAR)

可以看到和上面的公式有差別,這里RTTVAR就是上面的v,而SRTT就是上面的a。
其實這個公式和jacobson的公式是一樣的,只需要對公式做個變化。比如

1
SRTT <- (1 - alpha) * SRTT + alpha * R'

這個公式我們可以這樣子來:

1
SRTT <- SRTT - SRTT*alpha + alpha*R` => SRTT <- SRTT + alpha*(R` - SRTT)

於是我們可以看到這個公式就和上面的公式a是相同的了。

最后我們來看linux下的實現,linux的實現和jacobson所描述的偽碼基本一致,這里beta因子是1/4,而alpha因子是1/8.
linux下對應的計算是在tcp_rtt_estimator這個函數中,這個函數的第二個參數,就是當前最新的rtt值。linux的實現多了兩個東西,分別是mdev和medev_max, 其中mdev就是上面計算方法中的RTTVAR,而mdev_max則就是每次均差的最大值(linux最小是50毫秒).而在linux中RTTVAR也就等於mdev_max.

這里要注意一個東西,那就是RTT的計算是每次都會做的,而對於RTO來說是和數據段相關的,也就是說每次發送的時候估計rtt,然后當估算RTT時發送的段收到ack,之后就應該進行下一次估算了(也就是一個RTT過去了),也就是需要調節對應的rttvar,並且重新初始化一些值.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
     u32 srtt;       /* smoothed round trip time << 3  */
     u32 mdev;       /* medium deviation         */
     u32 mdev_max;   /* maximal mdev for the last rtt period */
     u32 rttvar;     /* smoothed mdev_max            */
     u32 rtt_seq;    /* sequence number to update rttvar */
 
static void tcp_rtt_estimator( struct sock *sk, const __u32 mrtt)
{
     struct tcp_sock *tp = tcp_sk(sk);
     long m = mrtt; /* RTT */
 
     /*  The following amusing code comes from Jacobson's
      *  article in SIGCOMM '88.  Note that rtt and mdev
      *  are scaled versions of rtt and mean deviation.
      *  This is designed to be as fast as possible
      *  m stands for "measurement".
      *
      *  On a 1990 paper the rto value is changed to:
      *  RTO = rtt + 4 * mdev
      *
      * Funny. This algorithm seems to be very broken.
      * These formulae increase RTO, when it should be decreased, increase
      * too slowly, when it should be increased quickly, decrease too quickly
      * etc. I guess in BSD RTO takes ONE value, so that it is absolutely
      * does not matter how to _calculate_ it. Seems, it was trap
      * that VJ failed to avoid. 8)
      */
     if (m == 0)
         m = 1;
     if (tp->srtt != 0) {
//開始更新a也就是srtt.可以看到因子為1/8.
         m -= (tp->srtt >> 3);  /* m is now error in rtt est */
         tp->srtt += m;       /* rtt = 7/8 rtt + 1/8 new */
//開始更新mean deviation.
         if (m < 0) {
             m = -m;     /* m is now abs(error) */
             m -= (tp->mdev >> 2);   /* similar update on mdev */
             /* This is similar to one of Eifel findings.
              * Eifel blocks mdev updates when rtt decreases.
              * This solution is a bit different: we use finer gain
              * for mdev in this case (alpha*beta).
              * Like Eifel it also prevents growth of rto,
              * but also it limits too fast rto decreases,
              * happening in pure Eifel.
              */
             if (m > 0)
                 m >>= 3;
         } else {
             m -= (tp->mdev >> 2);   /* similar update on mdev */
         }
         tp->mdev += m;           /* mdev = 3/4 mdev + 1/4 new */
//找到最大值付給rttvar
         if (tp->mdev > tp->mdev_max) {
             tp->mdev_max = tp->mdev;
             if (tp->mdev_max > tp->rttvar)
                 tp->rttvar = tp->mdev_max;
         }
 
//本次RTT的估算結束
         if (after(tp->snd_una, tp->rtt_seq)) {
//調節rttvar
             if (tp->mdev_max < tp->rttvar)
                 tp->rttvar -= (tp->rttvar - tp->mdev_max) >> 2;
             tp->rtt_seq = tp->snd_nxt;
//設置最小值
             tp->mdev_max = tcp_rto_min(sk);
         }
     } else {
//第一次進來的情況
         /* no previous measure. */
         tp->srtt = m << 3; /* take the measured time to be rtt */
         tp->mdev = m << 1; /* make sure rto = 3*rtt */
         tp->mdev_max = tp->rttvar = max(tp->mdev, tcp_rto_min(sk));
         tp->rtt_seq = tp->snd_nxt;
     }
}

最后來看一下RTO的計算,RTO的計算在__tcp_set_rto中,這個計算它是嚴格遵守rfc6298中的描述。

1
2
3
4
static inline u32 __tcp_set_rto( const struct tcp_sock *tp)
{
     return (tp->srtt >> 3) + tp->rttvar;
}

最后這里有一個要注意的地方,那就是rto的最小值,一般來說,在linux下,這個值是200ms,也就是說最少要200毫秒才會發生重傳,而這個值的修改是可以通過ip route來修改的,如何更改請看這里:

http://docs.redhat.com/docs/en-US/Red_Hat_Enterprise_Linux/4/html/Release_Notes/U7/ppc/ar01s04.html

而有關定時器退避這些,我在前面的blog有分析,感興趣的可以看我前面的blog.


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com