不同的 ACK 行為(減慢吞吐量?)
我在一台機器(opensolaris)上執行netio(http://freshmeat.net/projects/netio/)並聯繫兩台不同的Linux機器(都在2.6.18-128.el5上)機器A和機器B。機器A 使用 netio 的網路吞吐量為 10MB/秒,機器 B 使用 netio 的網路吞吐量為 100MB/秒。在打開的 solaris 上,我跟踪了連接,所有互動看起來都一樣 - 接收和發送的視窗大小相同,ssthresh 相同,擁塞視窗大小相同,但是慢速機器正在發送,並且每 2 或 3 次接收 ACK 而快速機器每接收 12 次就發送一個 ACK。三台機器都在同一個交換機上。這是 Dtrace 輸出:快速機器:
增量發送記錄 (us) bytes 字節 swnd snd_ws rwnd rcv_ws cwnd ssthresh 122 1448 \ 195200 7 131768 2 128872 1073725440 37 1448 \ 195200 7 131768 2 128872 1073725440 20 1448 \ 195200 7 131768 2 128872 1073725440 18 1448 \ 195200 7 131768 2 128872 1073725440 18 1448 \ 195200 7 131768 2 128872 1073725440 18 1448 \ 195200 7 131768 2 128872 1073725440 18 1448 \ 195200 7 131768 2 128872 1073725440 19 1448 \ 195200 7 131768 2 128872 1073725440 18 1448 \ 195200 7 131768 2 128872 1073725440 18 1448 \ 195200 7 131768 2 128872 1073725440 57 1448 \ 195200 7 131768 2 128872 1073725440 171 1448 \ 195200 7 131768 2 128872 1073725440 29 912 \ 195200 7 131768 2 128872 1073725440 30 / 0 195200 7 131768 2 128872 1073725440
慢機:
增量發送記錄 (us) bytes 字節 swnd snd_ws rwnd rcv_ws cwnd ssthresh 161 / 0 195200 7 131768 2 127424 1073725440 52 1448 \ 195200 7 131768 2 128872 1073725440 33 1448 \ 195200 7 131768 2 128872 1073725440 11 1448 \ 195200 7 131768 2 128872 1073725440 143 / 0 195200 7 131768 2 128872 1073725440 46 1448 \ 195200 7 131768 2 130320 1073725440 31 1448 \ 195200 7 131768 2 130320 1073725440 11 1448 \ 195200 7 131768 2 130320 1073725440 157 / 0 195200 7 131768 2 130320 1073725440 46 1448 \ 195200 7 131768 2 131768 1073725440 18 1448 \ 195200 7 131768 2 131768 1073725440
跟踪程式碼
dtrace:130717 在 CPU 0 上下降 #!/usr/sbin/dtrace -s #pragma D 選項安靜 #pragma D 選項預設參數 內聯 int TICKS=$1; 內聯字元串 ADDR=$$2; dtrace:::BEGIN { 計時器=(滴答聲!= NULL)?蜱:1; 滴答聲 = 計時器; 標題 = 10; 標題 = 0; walltime=時間戳; printf("正在啟動...\n"); } tcp:::發送 / ( args[2]->ip_daddr == ADDR || ADDR == NULL ) / { nfs[args[1]->cs_cid]=1; /* 這是一個 NFS 執行緒 */ delta=時間戳-walltime; walltime=時間戳; printf("%6d %8d \ %8s %8d %8d %8d %8d %8d %12d %12d %12d %8d %8d %d \n", 增量/1000, args[2]->ip_plength - args[4]->tcp_offset, "", args[3]->tcps_swnd, args[3]->tcps_snd_ws, args[3]->tcps_rwnd, args[3]->tcps_rcv_ws, args[3]->tcps_cwnd, args[3]->tcps_cwnd_ssthresh, args[3]->tcps_sack_fack, args[3]->tcps_sack_snxt, args[3]->tcps_rto, args[3]->tcps_mss, args[3]->tcps_retransmit ); 標誌=0; 標題 - ; } tcp:::receive / ( args[2]->ip_saddr == ADDR || ADDR == NULL ) && nfs[args[1]->cs_cid] / { delta=timestamp-walltime; walltime=時間戳; printf("%6d %8s / %8d %8d %8d %8d %8d %8d %12d %12d %12d %8d %8d %d \n", 增量/1000, "", args[2]->ip_plength - args[4]->tcp_offset, args[3]->tcps_swnd, args[3]->tcps_snd_ws, args[3]->tcps_rwnd, args[3]->tcps_rcv_ws, args[3]->tcps_cwnd, args[3]->tcps_cwnd_ssthresh, args[3]->tcps_sack_fack, args[3]->tcps_sack_snxt, args[3]->tcps_rto, args[3]->tcps_mss, args[3]->tcps_retransmit ); 標誌=0; 標題 - ; }
後續添加到包括未確認的字節數,結果證明慢速程式碼確實執行它是未確認的字節,直到它達到擁塞視窗,而快速機器永遠不會達到它的擁塞視窗。這是當未確認字節到達擁塞視窗時慢速機器的輸出:
unack unack delta bytes 發送 接收 cong ssthresh 字節字節我們發送接收視窗視窗視窗 發送 收到 139760 0 31 1448 \ 195200 131768 144800 1073725440 139760 0 33 1448 \ 195200 131768 144800 1073725440 144104 0 29 1448 \ 195200 131768 146248 1073725440 145552 0 31 / 0 195200 131768 144800 1073725440 145552 0 41 1448 \ 195200 131768 147696 1073725440 147000 0 30 / 0 195200 131768 144800 1073725440 147000 0 22 1448 \ 195200 131768 76744 72400 147000 0 28 / 0 195200 131768 76744 72400 147000 0 18 1448 \ 195200 131768 76744 72400 147000 0 26 / 0 195200 131768 76744 72400 147000 0 17 1448 \ 195200 131768 76744 72400 147000 0 27 / 0 195200 131768 76744 72400 147000 0 18 1448 \ 195200 131768 76744 72400 147000 0 56 / 0 195200 131768 76744 72400 147000 0 22 1448 \ 195200 131768 76744 72400
dtrace 程式碼:
#!/usr/sbin/dtrace -s #pragma D 選項安靜 #pragma D 選項預設參數 內聯 int TICKS=$1; 內聯字元串 ADDR=$$2; tcp:::send, tcp:::receive / ( args[2]->ip_daddr == ADDR || ADDR == NULL ) / { nfs[args[1]->cs_cid]=1; /* 這是一個 NFS 執行緒 */ delta=時間戳-walltime; walltime=時間戳; printf("%6d %6d %6d %8d \ %8s %8d %8d %8d %8d %8d %12d %12d %12d %8d %8d %d \n", args[3]->tcps_snxt - args[3]->tcps_suna , args[3]->tcps_rnxt - args[3]->tcps_rack, 增量/1000, args[2]->ip_plength - args[4]->tcp_offset, "", args[3]->tcps_swnd, args[3]->tcps_snd_ws, args[3]->tcps_rwnd, args[3]->tcps_rcv_ws, args[3]->tcps_cwnd, args[3]->tcps_cwnd_ssthresh, args[3]->tcps_sack_fack, args[3]->tcps_sack_snxt, args[3]->tcps_rto, args[3]->tcps_mss, args[3]->tcps_retransmit ); } tcp:::receive / ( args[2]->ip_saddr == ADDR || ADDR == NULL ) && nfs[args[1]->cs_cid] / { delta=timestamp-walltime; walltime=時間戳; printf("%6d %6d %6d %8s / %-8d %8d %8d %8d %8d %8d %12d %12d %12d %8d %8d %d \n", args[3]->tcps_snxt - args[3]->tcps_suna , args[3]->tcps_rnxt - args[3]->tcps_rack, 增量/1000, "", args[2]->ip_plength - args[4]->tcp_offset, args[3]->tcps_swnd, args[3]->tcps_snd_ws, args[3]->tcps_rwnd, args[3]->tcps_rcv_ws, args[3]->tcps_cwnd, args[3]->tcps_cwnd_ssthresh, args[3]->tcps_sack_fack, args[3]->tcps_sack_snxt, args[3]->tcps_rto, args[3]->tcps_mss, args[3]->tcps_retransmit ); }
現在仍然是一個問題,為什麼一台機器落後而另一台機器不落後……
我以前見過這樣的行為。我已經看到了兩個原因:
- 錯誤的 TCP/IP 流控制協商
- 壞司機
在您的情況下,TCP/IP 流控制問題的可能性較小,因為兩台機器都執行相同的核心,並且(設備核心模組除外,如果不同)因此執行相同的 TCP/IP 程式碼。
司機雖然。
不久前,我有一台 Windows 2003 伺服器,它根本無法將超過 6-10MB/s 的速度傳輸到某些伺服器,而且因為那是一個備份到磁碟的伺服器,這根本是不可接受的。在查看了一些數據包捕穫後,它們看起來很像您所看到的。解決的方法是將接收伺服器(Server 2003 備份伺服器)上的網路驅動程序(發生的 Broadcom)更新為更新的東西。完成後,我將獲得 60-80MB/s。
由於這是 Linux,您可能會遇到某種大段解除安裝問題。這在某種程度上確實依賴於 NIC 硬體本身處理大段的拆分。如果由於某種原因(壞韌體?)不起作用,它可能會導致這些奇怪的延遲。這是基於每個驅動程序或介面配置的。
ethtool -K
可以通過設備進行配置。