1
2
3
4
5
6
7
作者:李晓辉

联系方式:

1. 微信:Lxh_Chat

2. 邮箱:939958092@qq.com

前面两篇,我们分别聊了 TCP 缓冲区和 BDP,以及 MTU 从 1500 提升到 9000 的巨帧优化。

但网络性能调优还有两个容易被忽略的方向:CPU 为了省电,可能增加网络处理的唤醒延迟;网卡驱动和卸载功能的配置,也可能影响吞吐量、CPU 占用和延迟抖动。

今天我们就从这两个方向入手,看看有哪些值得关注的调优手段。

一、CPU 为了省电,为什么反而可能拖慢网络?

先想象一个场景:一台服务器平时负载不高,网络请求也不多,但偶尔会出现几毫秒甚至更高的延迟尖峰。

你检查了网络带宽、TCP 缓冲区和 MTU,似乎都没什么问题。这时候,不妨把目光转向 CPU 的空闲状态管理。

什么是 CPU C-state?

CPU 不仅有运行频率的高低,还有不同的工作状态和空闲状态。

C-state 就是 CPU 的空闲状态管理机制。CPU 暂时没有任务执行时,可以进入不同深度的空闲状态,以降低功耗。

可以简单理解为:

状态大致含义
C0CPU 正在执行指令
C1浅度空闲,通常能够较快恢复执行
C3更深的空闲状态,可能关闭更多内部单元
C6 及更深状态通常具有更高的节能效果,但退出延迟也可能更大

注意,具体有哪些状态、每个状态的功耗和退出延迟,都取决于 CPU 型号及平台实现,并不是每台机器都有相同的 C-state。

CPU 进入深度空闲状态后,能够节省更多电力。但当网卡收到数据,需要 CPU 处理软中断、协议栈和应用任务时,CPU 可能需要先退出空闲状态。

这段退出延迟,在高性能网络和低延迟业务中就可能成为一个影响因素。

例如,高频交易、实时通信、低延迟 RPC 等业务,不仅关注平均延迟,还关注 P99、P99.9 这样的尾延迟指标。即使平均延迟变化不大,偶发的唤醒延迟也可能影响业务体验。

不过,不要一看到延迟尖峰就认定是 C-state 导致的。网卡中断合并、CPU 调度、软中断积压、NUMA 跨节点访问等,同样可能造成延迟抖动。

怎么检查 C-state?

先查看 CPU 空闲状态统计:

安装一下工具

1
[root@host1 ~]# dnf install kernel-tools -y
1
2
[root@host1 ~]# cpupower monitor
WARNING: at least one cpu is offline

不同 CPU 平台和内核版本显示的监控项可能不同。执行后重点观察各个空闲状态的驻留时间,而不是只盯着某一个数字。

例如,如果 CPU 大部分时间都处于较深的空闲状态,并且业务又对唤醒延迟非常敏感,就值得进一步测试限制 C-state 后的变化。

如何限制 CPU 的空闲状态?

Linux 可以通过内核参数限制某些空闲驱动允许使用的状态。

但这里要注意:intel_idle 和 acpi_idle 是不同的 CPU 空闲驱动,不能把它们的参数混为一谈。

先查看当前使用的驱动:

1
2
[root@host1 ~]# cat /sys/devices/system/cpu/cpuidle/current_driver
none

我这里的none这表示当前内核没有报告正在使用的 CPU idle 驱动。

如果输出是 intel_idle,说明当前使用 Intel 的专用空闲驱动;如果输出是 acpi_idle,则使用 ACPI 空闲驱动。

对于使用 intel_idle 的系统,可以考虑通过 intel_idle.max_cstate 限制可使用的空闲状态。例如:

1
2
[root@host1 ~]# grubby --update-kernel=ALL --args="intel_idle.max_cstate=1"

对于使用 acpi_idle 的系统,可以考虑:

1
[root@host1 ~]# grubby --update-kernel=ALL --args="processor.max_cstate=1"

这两组参数应根据实际驱动选择,不要不加判断地同时配置。

这里设置为 1,主要是限制空闲状态的深度,并不意味着 CPU 永远处于 C0,也不代表所有平台都会表现为完全相同的 C-state。

修改后需要重启,并验证内核启动参数及实际空闲状态:

1
2
3
cat /proc/cmdline
cat /sys/devices/system/cpu/cpuidle/current_driver
cpupower monitor

生产环境操作前,建议记录原始参数并准备回滚方案。

另外,限制 C-state 往往会增加功耗和发热。对于普通业务服务器,不建议为了追求更低延迟就一律禁用深度空闲状态。

使用 tuned 控制空闲状态延迟

如果希望更灵活地控制 CPU 空闲状态,可以研究 RHEL 提供的 tuned。

先查看当前配置:

1
2
[root@host1 ~]# tuned-adm active
[root@host1 ~]# tuned-adm list

创建一个独立的调优配置:

1
2
3
4
5
6
7
8
9
10
mkdir -p /etc/tuned/profiles/network-latency-custom

cat > /etc/tuned/profiles/network-latency-custom/tuned.conf <<'EOF'
[main]
summary=Customized network latency tuning
include=network-latency

[cpu]
force_latency=cstate.id:1|2
EOF

这里的 force_latency 用于通过 CPU 调优插件约束空闲状态的退出延迟。cstate.id:1 是一种按状态索引指定的方式,但索引对应的具体状态取决于平台,不能直接认定所有机器上的 1 都代表同一个 C-state。

应用之前,先确认当前 RHEL 10 的 tuned 版本支持该配置,并结合实际 CPU 状态验证:

1
2
3
[root@host1 ~]# tuned-adm profile network-latency-custom
[root@host1 ~]# tuned-adm active
[root@host1 ~]# cpupower monitor

如果配置不被支持,或者状态变化与预期不符,就不要直接将其投入生产。

需要注意,force_latency 不是简单地把 CPU 锁定在某个状态,也不是完全禁止 C-state。它的目标是约束空闲状态退出延迟,最终行为取决于平台和内核实现。

NIC 驱动参数怎么改,而且重启不丢?

CPU 状态看完了,再来看网卡。

同样一张网卡,在不同驱动参数下,可能表现出不同的中断延迟、功耗、吞吐量和 CPU 占用。

先确定网卡使用什么驱动

使用 ethtool -i:

1
2
3
4
5
6
7
8
9
10
11
12
[root@host1 ~]# ethtool -i ens160
driver: vmxnet3
version: 1.9.0.0-k-NAPI
firmware-version:
expansion-rom-version:
bus-info: 0000:03:00.0
supports-statistics: yes
supports-test: no
supports-eeprom-access: no
supports-register-dump: yes
supports-priv-flags: no

这里的 vmxnet3 是网卡驱动名称。实际生产环境也可能是 ixgbe、i40e、ice、bnxt_en 或虚拟网卡驱动 virtio_net、vmxnet3 等。

不同驱动支持的参数完全不同。

接下来可以使用 modinfo 查看模块参数:

1
modinfo -p vmxnet3

例如,某些驱动会提供中断延迟、调试或节能相关参数。但不要直接照搬其他驱动的配置:参数名称、取值范围、默认值和适用硬件都必须以当前驱动为准。

对于虚拟机,还需要注意:来宾操作系统看到的可能是虚拟网卡,物理网卡驱动参数则由宿主机或云平台管理。

如何持久化驱动模块参数?

假设当前驱动确实支持某个名为 ExampleParam 的参数,可以在 /etc/modprobe.d/ 中建立配置文件:

1
2
3
cat > /etc/modprobe.d/example-net.conf <<'EOF'
options example_driver ExampleParam=1
EOF

上面是演示语法,example_driver 和 ExampleParam 需要替换为真实驱动及其支持的参数,不能原样照抄执行。

这种方式适用于通过模块加载机制传入的参数。并不是所有网卡设置都属于模块参数,也不是所有参数都能在模块加载后修改。

修改后,应该先确认参数的生效方式。某些参数需要重新加载驱动,甚至需要重启系统。

特别提醒:不要在远程 SSH 会话中贸然执行 modprobe -r 卸载正在使用的网卡驱动。 如果 SSH 就是通过这张网卡连接的,卸载驱动可能直接切断连接;生产服务器应使用带外管理、云控制台等可靠的恢复途径。

如果确实需要重新加载驱动,必须先确认没有其他网卡或关键业务依赖该模块,并安排维护窗口。

网卡卸载功能:让硬件替 CPU 干活

最后来看一个更常见、也更容易实测的优化方向:网卡卸载。

现代网卡可以替 CPU 完成部分网络处理工作,例如 TCP 分段、校验和计算以及接收数据包合并。

这些功能可以减少 CPU 在逐包处理上花费的时间,尤其是在高吞吐量场景下。

先查看当前卸载状态

使用 ethtool -k:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
[root@host1 ~]# ethtool -k ens160
Features for ens160:
rx-checksumming: on
tx-checksumming: on
tx-checksum-ipv4: off [fixed]
tx-checksum-ip-generic: on
tx-checksum-ipv6: off [fixed]
tx-checksum-fcoe-crc: off [fixed]
tx-checksum-sctp: off [fixed]
scatter-gather: on
tx-scatter-gather: on
tx-scatter-gather-fraglist: off [fixed]
tcp-segmentation-offload: on
tx-tcp-segmentation: on
tx-tcp-ecn-segmentation: off [fixed]
tx-tcp-mangleid-segmentation: off
tx-tcp6-segmentation: on
generic-segmentation-offload: on
generic-receive-offload: on
large-receive-offload: on
rx-vlan-offload: on
tx-vlan-offload: on
ntuple-filters: off [fixed]
receive-hashing: on
highdma: on
rx-vlan-filter: on [fixed]
vlan-challenged: off [fixed]
tx-gso-robust: off [fixed]
tx-fcoe-segmentation: off [fixed]
tx-gre-segmentation: off [fixed]
tx-gre-csum-segmentation: off [fixed]
tx-ipxip4-segmentation: off [fixed]
tx-ipxip6-segmentation: off [fixed]
tx-udp_tnl-segmentation: off [fixed]
tx-udp_tnl-csum-segmentation: off [fixed]
tx-gso-partial: off [fixed]
tx-tunnel-remcsum-segmentation: off [fixed]
tx-sctp-segmentation: off [fixed]
tx-esp-segmentation: off [fixed]
tx-udp-segmentation: off [fixed]
tx-gso-list: off [fixed]
tx-nocache-copy: off
loopback: off [fixed]
rx-fcs: off [fixed]
rx-all: off [fixed]
tx-vlan-stag-hw-insert: off [fixed]
rx-vlan-stag-hw-parse: off [fixed]
rx-vlan-stag-filter: off [fixed]
l2-fwd-offload: off [fixed]
hw-tc-offload: off [fixed]
esp-hw-offload: off [fixed]
esp-tx-csum-hw-offload: off [fixed]
rx-udp_tunnel-port-offload: off [fixed]
tls-hw-tx-offload: off [fixed]
tls-hw-rx-offload: off [fixed]
rx-gro-hw: off [fixed]
tls-hw-record: off [fixed]
rx-gro-list: off
macsec-hw-offload: off [fixed]
rx-udp-gro-forwarding: off
hsr-tag-ins-offload: off [fixed]
hsr-tag-rm-offload: off [fixed]
hsr-fwd-offload: off [fixed]
hsr-dup-offload: off [fixed]
[root@host1 ~]#

重点关注这些项目:

1
2
3
4
tcp-segmentation-offload: on
generic-receive-offload: on
tx-checksumming: on
rx-checksumming: on

这里的输出仅用于说明常见项目,实际结果取决于网卡和驱动。

几个常见功能的作用如下:

功能作用
TSO发送端将较大的 TCP 数据交给网卡分段
GRO接收端合并符合条件的数据包,减少协议栈处理次数
TX checksum offload由网卡协助计算发送方向的校验和
RX checksum offload由网卡协助验证接收方向的校验和

举个例子:如果应用要发送大量 TCP 数据,启用 TSO 后,内核可以把较大的数据块交给网卡,由网卡完成后续分段。

这样,CPU 不必亲自处理每一个线上 TCP 段,有机会降低处理开销。

但要注意,TSO、GRO 等机制与 MTU 是不同维度的优化。即使 MTU 仍然是 1500,网卡也可能通过卸载功能减少 CPU 负担。

如何临时修改卸载功能?

例如,临时关闭 TSO:

1
[root@host1 ~]# ethtool -K ens160 tso off

重新开启:

1
[root@host1 ~]# ethtool -K ens160 tso on

查看当前状态:

1
2
3
4
5
6
[root@host1 ~]# ethtool -k ens160 | grep -E 'tcp-segmentation-offload|generic-receive-offload|tx-checksumming|rx-checksumming'
rx-checksumming: on
tx-checksumming: on
tcp-segmentation-offload: off
generic-receive-offload: on

这些设置通常用于临时测试或故障排查。

例如,如果怀疑某个驱动的卸载功能存在兼容性问题,可以在可控环境中关闭对应功能,比较修改前后的吞吐量、CPU 使用率和错误计数。

但不要为了“优化”就把所有卸载功能全部关闭。很多情况下,这样反而会增加 CPU 开销、降低吞吐量。

如何让卸载配置持久生效?

通过 ethtool -K 修改的功能状态通常不会自动成为永久配置。要持久化,可以使用 NetworkManager 支持的连接配置属性。

先查找网卡对应的连接配置名称:

1
2
3
4
[root@host1 ~]# nmcli connection show
NAME UUID TYPE DEVICE
ens160 08864eb2-7927-3a89-b954-4dbabc5db128 ethernet ens160
lo 483ae556-41bc-42a8-baa0-df0031b3ab88 loopback lo

假设连接配置名称确实是 ens160,并且当前 NetworkManager 版本支持 TSO 对应的 ethtool 属性,可以尝试:

1
[root@host1 ~]# nmcli connection modify ens160 ethtool.feature-tso on

然后重新激活连接:

1
[root@host1 ~]# nmcli connection down ens160 && nmcli connection up ens160

最后验证:

1
2
[root@host1 ~]# ethtool -k ens160 | grep tcp-segmentation-offload
tcp-segmentation-offload: on

这里有个细节:连接配置名称不一定等于网卡设备名,而且 NetworkManager 并非在所有版本中都支持相同的 ethtool.feature-* 属性。

如果命令提示属性不支持,应先检查本机版本的 nmcli 帮助和 NetworkManager 文档,不要直接认定这是 RHEL 10 所有环境通用的语法。

此外,持久化配置最终能否生效,还取决于驱动是否支持该功能,以及 NetworkManager 是否成功应用了连接配置。

怎么判断调优真的有效?

这篇文章涉及的三个方向,最终都应该通过测试验证,而不是仅仅看配置是否修改成功。

建议每次只调整一个变量,并在相同的负载、网卡速率和测试时长下进行对比。

可以重点观察:

  • 吞吐量: 使用 iperf3 对比网络传输能力。
  • CPU 使用率: 观察完成相同传输任务所需的 CPU 资源。
  • 延迟分布: 关注平均延迟以及 P95、P99 等尾延迟。
  • 中断与软中断: 结合 /proc/interrupts、/proc/softirqs 观察变化。
  • 功耗与温度: 特别是限制 C-state 后,要确认额外的能耗是否值得。

例如:

1
2
3
4
5
6
7
8
# 查看网卡中断分布
cat /proc/interrupts

# 查看网络软中断统计
cat /proc/softirqs

# 查看网卡统计信息
ip -s link show ens160

如果要验证 CPU 空闲状态对延迟的影响,可以在业务负载尽量一致的情况下,比较调优前后的延迟分布,而不是只执行一次 ping 就下结论。

对于 NIC 卸载功能,也应优先在测试环境中验证,确认没有吞吐量下降、丢包或兼容性问题后,再考虑推广到生产环境。

总结

这篇我们主要关注三个方向:

  1. CPU C-state: 深度空闲状态可能增加唤醒延迟,但是否影响业务,需要结合 CPU 平台和延迟测试判断。
  2. NIC 驱动参数: 使用 ethtool -i 和 modinfo -p 了解驱动及其参数,再根据驱动支持情况选择持久化方式。
  3. 网卡卸载功能: 使用 ethtool -k 查看状态、ethtool -K 进行临时测试,确认 NetworkManager 支持后再配置持久化。

网络调优不是把所有参数都调到最大,也不是把所有省电功能都关掉。

先找到瓶颈,再一次调整一个参数,最后用吞吐量、CPU 占用和尾延迟验证效果。 这才是生产环境中更可靠的调优方法。