我正在运行 netio (http://freshmeat.net/projects/netio/) 在一台机器 (opensolaris) 上并联系两台不同的 Linux 机器(均在 2.6.18-128.el5 上),机器 A 和机器 B。机器 A 使用 netio 的网络吞吐量为 10MB/秒,机器 B 使用 netio 的网络吞吐量为 100MB/秒。在 open solaris 上,我对连接进行了 dtrace,所有交互看起来都相同 - 接收和发送的窗口大小相同,ssthresh 相同,拥塞窗口大小相同,但慢速机器每 2 或 3 次接收发送一次 ACK,而快速机器每 12 次接收发送一次 ACK。所有三台机器都在同一台交换机上。以下是 Dtrace 输出:快速机器:
delta 发送 接收 (美国)字节字节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
慢速机器:
delta 发送 接收 (美国)字节字节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 代码
dtrace:130717 在 CPU 0 上丢失 /usr/sbin/dtrace -s #pragma D option quiet #pragma D 选项默认参数 内联 int TICKS=$1; 内联字符串ADDR=$$2; dtrace:::BEGIN { 计时器 = (TICKS != NULL )?TICKS : 1; 滴答=计时器; 标题 = 10; 标题=0; 墙上时间=时间戳; printf("正在启动...\n"); } tcp:::发送 /(args[2]->ip_daddr == ADDR || ADDR == NULL)/ { nfs[args[1]->cs_cid]=1; /*这是一个 NFS 线程 */ delta=时间戳-墙上时间; 墙上时间=时间戳; printf("%6d %8d \ %8s %8d %8d %8d %8d %8d %12d %12d %12d %8d %8d %d \n", 增量/1000, 参数[2]->ip_plength - 参数[4]->tcp_offset, “” 参数[3]->tcps_swnd, 参数[3]->tcps_snd_ws, 参数[3]->tcps_rwnd, 参数[3]->tcps_rcv_ws, 参数[3]->tcps_cwnd, 参数[3]->tcps_cwnd_ssthresh, 参数[3]->tcps_sack_fack, 参数[3]->tcps_sack_snxt, 参数[3]->tcps_rto, 参数[3]->tcps_mss, 参数[3]->tcps_retransmit (英文): 标志=0; 标题 - ; } tcp:::接收 /(args[2]->ip_saddr == ADDR || ADDR == NULL)&& nfs[args[1]->cs_cid] / { delta=时间戳-挂钟时间; 墙上时间=时间戳; printf("%6d %8s / %8d %8d %8d %8d %8d %8d %12d %12d %12d %8d %8d %d \n", 增量/1000, “” 参数[2]->ip_plength - 参数[4]->tcp_offset, 参数[3]->tcps_swnd, 参数[3]->tcps_snd_ws, 参数[3]->tcps_rwnd, 参数[3]->tcps_rcv_ws, 参数[3]->tcps_cwnd, 参数[3]->tcps_cwnd_ssthresh, 参数[3]->tcps_sack_fack, 参数[3]->tcps_sack_snxt, 参数[3]->tcps_rto, 参数[3]->tcps_mss, 参数[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 option quiet #pragma D 选项默认参数 内联 int TICKS=$1; 内联字符串ADDR=$$2; tcp:::发送,tcp:::接收 /(args[2]->ip_daddr == ADDR || ADDR == NULL)/ { nfs[args[1]->cs_cid]=1; /*这是一个 NFS 线程 */ delta=时间戳-墙上时间; 墙上时间=时间戳; printf("%6d %6d %6d %8d \ %8s %8d %8d %8d %8d %8d %12d %12d %12d %8d %8d %d \n", 参数[3]->tcps_snxt - 参数[3]->tcps_suna 参数[3]->tcps_rnxt - 参数[3]->tcps_rack, 增量/1000, 参数[2]->ip_plength - 参数[4]->tcp_offset, “” 参数[3]->tcps_swnd, 参数[3]->tcps_snd_ws, 参数[3]->tcps_rwnd, 参数[3]->tcps_rcv_ws, 参数[3]->tcps_cwnd, 参数[3]->tcps_cwnd_ssthresh, 参数[3]->tcps_sack_fack, 参数[3]->tcps_sack_snxt, 参数[3]->tcps_rto, 参数[3]->tcps_mss, 参数[3]->tcps_retransmit (英文): } tcp:::接收 /(args[2]->ip_saddr == ADDR || ADDR == NULL)&& nfs[args[1]->cs_cid] / { delta=时间戳-挂钟时间; 墙上时间=时间戳; printf("%6d %6d %6d %8s / %-8d %8d %8d %8d %8d %8d %12d %12d %12d %8d %8d %d \n", 参数[3]->tcps_snxt - 参数[3]->tcps_suna 参数[3]->tcps_rnxt - 参数[3]->tcps_rack, 增量/1000, “” 参数[2]->ip_plength - 参数[4]->tcp_offset, 参数[3]->tcps_swnd, 参数[3]->tcps_snd_ws, 参数[3]->tcps_rwnd, 参数[3]->tcps_rcv_ws, 参数[3]->tcps_cwnd, 参数[3]->tcps_cwnd_ssthresh, 参数[3]->tcps_sack_fack, 参数[3]->tcps_sack_snxt, 参数[3]->tcps_rto, 参数[3]->tcps_mss, 参数[3]->tcps_retransmit (英文): }
现在仍然是一个问题,为什么一台机器落后而另一台没有落后......
答案1
我以前也见过类似的行为。我发现有两个原因:
- TCP/IP 流控制协商不良
- 糟糕的司机
在您的情况下,TCP/IP 流控制问题不太可能发生,因为两台机器都运行相同的内核(如果不同,则设备内核模块除外),因此运行相同的 TCP/IP 代码。
不过,是司机。
不久前,我有一台 Windows 2003 服务器,它无法向某些服务器传输超过 6-10MB/s 的数据,而且由于这是一台备份到磁盘的服务器,这简直是不可接受的。查看了一些数据包捕获后,它们看起来非常像您看到的内容。修复方法是将接收服务器(Server 2003 备份服务器)上的网络驱动程序(碰巧是 Broadcom)更新为较新的版本。完成此操作后,我获得了 60-80MB/s 的数据。
由于这是 Linux,你可能会遇到大段卸载某种问题。这在一定程度上依赖于 NIC 硬件本身处理大段的分割。如果由于某种原因(固件不好?)无法正常工作,则会导致这些奇怪的延迟。这是基于每个驱动程序或接口配置的。ethtool -K
可以通过设备进行配置。