linux修改重传次数,聊一聊重传次数
在RTO的計算方法中,介紹了RFC6298對于RTO的計算和RTO timer的管理算法。
但有一個重要的問題RFC沒有提到,那就是如果出現了超時重傳,那重傳多少次可以放棄呢?
當然這是一個實現相關的細節,不同的操作系統可能有不同的實現策略。
在這篇wiki中,就來介紹一下Linux中是怎么限制超時重傳次數的。
聽說Linux有兩個參數限制超時重傳次數
沒錯,Linux中確實定義了兩個參數來限定超時重傳的次數的,以下是源碼中Documentation/networking/ip-sysctl.txt文檔中的描述
tcp_retries1 - INTEGER
This value influences the time, after which TCP decides, that
something is wrong due to unacknowledged RTO retransmissions,
and reports this suspicion to the network layer.
See tcp_retries2 for more details.
RFC 1122 recommends at least 3 retransmissions, which is the
default.
tcp_retries2 - INTEGER
This value influences the timeout of an alive TCP connection,
when RTO retransmissions remain unacknowledged.
Given a value of N, a hypothetical TCP connection following
exponential backoff with an initial RTO of TCP_RTO_MIN would
retransmit N times before killing the connection at the (N+1)th RTO.
The default value of 15 yields a hypothetical timeout of 924.6
seconds and is a lower bound for the effective timeout.
TCP will effectively time out at the first RTO which exceeds the
hypothetical timeout.
RFC 1122 recommends at least 100 seconds for the timeout,
which corresponds to a value of at least 8.
就是這樣一段話,可能由于過于概括,會令人產生很多疑問,甚至產生一些誤解。
比如常見的問題有:
a. 超過tcp_retries1這個閾值后,到底是report了怎樣一種suspicion呢?
b. tcp_retries1和tcp_retries2的數字是表示RTO重傳的次數上限,對嗎?
c. 文檔中提到,924.6s is a lower bound for the effective timeout。
這里的effective timeout是指什么?
為什么是lower bound,tcp_retries2不應該是限制重傳次數的upper bound嗎?
下面就結合Linux 3.10的源碼來逐個解釋一下以上幾個問題。并在最后給出一個總結。
重傳超過tcp_retries1會怎樣
文檔中說的suspicion到底是什么呢?來看一下tcp_retries1相關的代碼部分
// RTO timer的處理函數是tcp_retransmit_timer(),與tcp_retries1相關的代碼調用關系如下
tcp_retransmit_timer()
=> tcp_write_timeout() // 判斷是否重傳了足夠的久
=> retransmit_timed_out(sk, sysctl_tcp_retries1, 0, 0) // 判斷是否超過了閾值
// tcp_write_timeout()的具體相關內容
...
if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) {
// 如果超時發生在三次握手期間,此時有專門的tcp_syn_retries來負責限定重傳次數
...
} else { // 如果超時發生在數據發送期間
// 這個函數負責判斷重傳是否超過閾值,返回真表示超過。后續會詳細分析這個函數
if (retransmits_timed_out(sk, sysctl_tcp_retries1, 0, 0)) {
/* Black hole detection */
tcp_mtu_probing(icsk, sk); // 如果開啟tcp_mtu_probing(默認關閉)了,則執行PMTU
dst_negative_advice(sk); // 更新路由緩存
}
...
}
從以上的代碼可以看到,一旦重傳超過閾值tcp_retries1,主要的動作就是更新路由緩存。
用以避免由于路由選路變化帶來的問題。
重傳超過tcp_retries2會怎樣
會直接放棄重傳,關閉TCP流
// 依然還是在tcp_write_timeout()中,retry_until一般是tcp_retries2
...
if (retransmits_timed_out(sk, retry_until, syn_set ? 0 : icsk->icsk_user_timeout, syn_set)) {
/* Has it gone just too far? */
tcp_write_err(sk); // 調用tcp_done關閉TCP流
return 1;
}
retries限制的重傳次數嗎
咋一看文檔,很容易想到retries的數字就是限定的重傳的次數,甚至源碼中對于retries常量注釋中都寫著”This is how many retries it does…”
#define TCP_RETR1 3 /*
* This is how many retries it does before it
* tries to figure out if the gateway is
* down. Minimal RFC value is 3; it corresponds
* to ~3sec-8min depending on RTO.
*/
#define TCP_RETR2 15 /*
* This should take at least
* 90 minutes to time out.
* RFC1122 says that the limit is 100 sec.
* 15 is ~13-30min depending on RTO.
*/
那就就來看看retransmits_timed_out的具體實現,看看到底是不是限制的重傳次數
/* This function calculates a "timeout" which is equivalent to the timeout of a
* TCP connection after "boundary" unsuccessful, exponentially backed-off
* retransmissions with an initial RTO of TCP_RTO_MIN or TCP_TIMEOUT_INIT if
* syn_set flag is set.
*/
static bool retransmits_timed_out(struct sock *sk,
unsigned int boundary,
unsigned int timeout,
bool syn_set)
{
unsigned int linear_backoff_thresh, start_ts;
// 如果是在三次握手階段,syn_set為真
unsigned int rto_base = syn_set ? TCP_TIMEOUT_INIT : TCP_RTO_MIN;
if (!inet_csk(sk)->icsk_retransmits)
return false;
// retrans_stamp記錄的是數據包第一次發送的時間,在tcp_retransmit_skb()中設置
if (unlikely(!tcp_sk(sk)->retrans_stamp))
start_ts = TCP_SKB_CB(tcp_write_queue_head(sk))->when;
else
start_ts = tcp_sk(sk)->retrans_stamp;
// 如果用戶態未指定timeout,則算一個出來
if (likely(timeout == 0)) {
/* 下面的計算過程,其實就是算一下如果以rto_base為第一次重傳間隔,
* 重傳boundary次需要多長時間
*/
linear_backoff_thresh = ilog2(TCP_RTO_MAX/rto_base);
if (boundary <= linear_backoff_thresh)
timeout = ((2 << boundary) - 1) * rto_base;
else
timeout = ((2 << linear_backoff_thresh) - 1) * rto_base +
(boundary - linear_backoff_thresh) * TCP_RTO_MAX;
}
// 如果數據包第一次發送的時間距離現在的時間間隔,超過了timeout值,則認為重傳超于閾值了
return (tcp_time_stamp - start_ts) >= timeout;
}
從以上的代碼分析可以看到,真正起到限制重傳次數的并不是真正的重傳次數。
而是以tcp_retries1或tcp_retries2為boundary,以rto_base(如TCP_RTO_MIN 200ms)為初始RTO,計算得到一個timeout值出來。如果重傳間隔超過這個timeout,則認為超過了閾值。
上面這段話太繞了,下面舉兩個個例子來說明
以判斷是否放棄TCP流為例,如果tcp_retries2=15,那么計算得到的timeout=924600ms。
1. 如果RTT比較小,那么RTO初始值就約等于下限200ms
由于timeout總時長是924600ms,表現出來的現象剛好就是重傳了15次,超過了timeout值,從而放棄TCP流
2. 如果RTT較大,比如RTO初始值計算得到的是1000ms
那么根本不需要重傳15次,重傳總間隔就會超過924600ms。
比如我測試的一個RTT=400ms的情況,當tcp_retries2=10時,僅重傳了3次就放棄了TCP流
另外幾個小問題
理解了Linux決定重傳次數的真實機制,就不難回答一下幾個問題了
>> effective timeout指的是什么?
<< 就是retransmits_timed_out計算得到的timeout值
>> 924.6s是怎么算出來的?
<< 924.6s = (( 2 << 9) -1) * 200ms + (15 - 9) * 120s
>> 為什么924.6s是lower bound?
<< 重傳總間隔必須大于timeout值,即 (tcp_time_stamp - start_ts) >= timeout
>> 那RTO超時的間隔到底是不是源碼注釋的"15 is ~13-30min depending on RTO."呢?
<< 顯然不是! 雖然924.6s(15min)是一個lower bound,但是它同時也是一個upper bound!
怎么理解?舉例說明
1. 如果某個RTO值導致,在已經重傳了14次后,總重傳間隔開銷是924s
那么它還需要重傳第15次,即使離924.6s只差0.6s。這就是發揮了lower bound的作用
2. 如果某個RTO值導致,在重傳了10次后,總重傳間隔開銷是924s
重傳第11次后,第12次超時觸發時計算得到的總間隔變為1044s,超過924.6s
那么此時會放棄第12次重傳,這就是924.6s發揮了upper bound的作用
總的來說,在Linux3.10中,如果tcp_retres2設置為15。總重傳超時周期應該在如下范圍內
[924.6s, 1044.6s)
所以綜合上述,Linux并不是直接拿tcp_retries1和tcp_retries2來限制重傳次數的,而是用計算得到
的一個timeout值來判斷是否要放棄重傳的。真正的重傳次數同時與RTT相關。
總結
以上是生活随笔為你收集整理的linux修改重传次数,聊一聊重传次数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电脑经常蓝屏变黑屏怎么办啊 电脑频繁蓝屏
- 下一篇: 怎么更改u盘启动盘格式 修改U盘启动盘的