之前我們介紹的都是協議中給出的RTO計算方法,下面我們看一下linux實現中RTO的計算方法。在linux中維護了srtt、mdev、mdev_max、rttvar、rtt_seq幾個狀態變量用來計算RTO,其中linux實現中的mdev變量相當於協議中的RTTVAR變量。rtt_seq狀態變量用來控制一個RTT時間窗,linux在一個RTT時間窗內部更新狀態變量的方式與RTT時間窗結束更新狀態變量的方式不同,rtt_seq則用來判斷當前是在RTT時間窗內部,還是一個RTT時間窗已經結束。
一、RTT時間窗的判斷
幾個狀態變量中的rtt_seq用來判斷當前采樣是否處於RTT時間窗內,我們簡單的說一下如何判斷
在TCP窗口管理時候維護發送窗口有兩個狀態變量,一個是snd.una,另外一個是snd.nxt。其中snd.una表示還沒有被ACK確認的數據包里面最早的系列號,snd.nxt表示下一個待發送的數據包。初始的時候設置rtt_seq =snd.nxt,隨着數據包的發送和ACK報文的接收,snd.una和snd.nxt都會向前滑行,當更新RTT狀態變量的時候,如果發現snd.una<=rtt_seq,說明之前發送的數據包還沒有收到ACK,當前還處於RTT時間窗內部。如果發現snd.una>rtt_seq說明之前發送的數據包已經收到了對應的ACK確認,那么一個RTT時間窗結束,並把rtt_seq設置為snd.nxt繼續下一個RTT時間窗的處理。
二、狀態變量的更新
1、在測量到第一個RTT采樣之前,linux會先查看本地緩存中是否由目標ip地址的RTT緩存信息,如果由對應的緩存信息,則會根據緩存信息初始化RTO,如果沒有對應的緩存信息,則會把RTO初始化為3s。
我們可以使用ip tcp_metrics查看有所緩存,也可以使用ip tcp_metrics show ip命令查看某個ip地址相關的緩存信息
******@Inspiron:~$ ip tcp_metrics show 121.201.104.55
121.201.104.55 age 56.604sec cwnd 10 rtt 461481us rttvar 461481us source 192.168.1.103
srtt = m
mddev = m/2
rttvar = max(mdev, min_rto)
mdev_max = rttvar
RTO = srtt + 4 * rttvar
其中min_rto為該目標地址的最小RTT,如果路由中有配置那么使用配置值,如果沒有配置則使用TCP_RTO_MIN,TCP_RTO_MIN常量為50ms(linux代碼中這個常量為200ms實際為放大四倍后的值)。
3、在隨后再次收到RTT測量值m的時候,按照如下更新mdev
if(m < (srtt - mdev))
mdev = (31/32) * mdev + (1/32) * |srtt - m|
else
mdev = (3/4) * mdev + (1/4) * |srtt - m|
我們之前說過linux實現中mdev變量相當於協議中的RTTVAR變量,這里mdev的更新與協議有了明顯的不同,主要原因是如果鏈路時延突然大幅降低的時候,如果按照協議方法更新反而會大幅增加最后的RTO,因此linux在發現鏈路時延大幅下降的時候會降低RTO增長的幅度。其他狀態變量更新如下
mdev_max = max(mdev_max, mdev)
- srtt = (7/8) * srtt + (1/8) * m
- rttvar = mdev_max
- RTO = srtt + 4 * rttvar
4、根據rtt_seq來判斷如果當前是RTT時間窗結束,則執行如下流程
if(mdev_max < rtt_var)
{
rtt_var = (3/4) * rtt_var + (1/4) * mdev_max
}
mdev_max = min_rto
補充說明:
1、linux中在測量到第一個RTT采樣之前RTO的初始化參考tcp_init_metrics,RTT相關狀態變量的更新參考tcp_rtt_estimator,其中mdev放大4倍,srtt放大8倍進行的保存和計算。
