TCP是一個複雜的協議,這種複雜來源於對報文傳輸的可靠性承諾。對每條TCP連接來說,除了有獨立的狀態機、定時器之外,還有擁塞控制相關的一些運行變量,比如RTT、CWND、SSTHRESH等,這些運行參數同樣也是每連接(Per-Connection
)的
Per-Connection
意味着每條連接的這些參數互不影響,這是理所應當的!但是,想想這個情景:A與B之間已經建立了一條穩定的TCP連接,此時若新建一條新的連接,它的參數該如何設置呢?顯然,和原連接保持一致是個快速達到穩定的辦法。這就好比一個人要去一個陌生的地方,卻不知道該選擇哪種交通工具,也不知道該預估多少時間,對他來說,汲取去過的人的經驗總是一條捷徑。
這就是Linux內核中TCP Metrics
框架的作用,它可以爲後續的連接提供指導。當主機之間需要頻繁建立和拆除TCP連接時,它帶來的好處更加明顯。
TCP Metrics
顯然不能是Per-Connection
的,而應該是Per-Host
的。也就是說,TCP Metrics
表項應該是基於<源IP,目的IP>
二元組的。從一臺主機的角度,到達另一個特定地址主機的網絡鏈路狀況應該是被兩臺主機之間的所有連接所共享的。
內核使用tcp_metrics_block
表示一條Metrics
表項,這些表項根據<源IP,目的IP>
組織在tcp_metrics_hash
衝突鏈表表中,記錄的值保存在內部tcpm_vals
數組
struct tcp_metrics_block {
struct tcp_metrics_block __rcu *tcpm_next;
struct inetpeer_addr tcpm_saddr;
struct inetpeer_addr tcpm_daddr;
......
u32 tcpm_vals[TCP_METRIC_MAX_KERNEL + 1];
......
};
當新建TCP連接時,內核使用下面的接口來爲TCP套接字設置TCP Metrics
指導下的參數
void tcp_init_metrics(struct sock *sk)
當某條TCP連接收的運行參數發生變化時,比如重新計算RTT了,內核會使用下面的接口來更新它對應的TCP Metrics
表項。切記,TCP Metrics
表項是Per-Host
的,因此,多條TCP連接的套接字可能會更新同一條表項。
void tcp_update_metrics(struct sock *sk)
內核同樣提供ip-tcp_metrics命令查看主機上的TCP Metrics
表項.