上一篇 下一篇 分享链接 返回 返回顶部

高并发下CPU软中断占用异常飙升?如何通过内核参数优化RPS/RFS与中断亲和性?

发布人:Minchunlin 发布时间:2025-08-02 09:56 阅读量:1311


我是负责公司核心网关服务的一名运维兼内核调优工程师。前不久,我们的业务迎来了一次流量洪峰,网关服务器明明 CPU 负载不高,业务进程也很健康,但延迟却突然飙升,吞吐掉了一半。

我第一时间 SSH 上去,一看 top,心里咯噔一下——软中断(ksoftirqd)占用了接近 80% 的 CPU,而用户态进程几乎没在跑。sar -n DEV 和 mpstat -P ALL 1 更让我确信:网络包完全压在了软中断处理上。

这次故障让我第一次在真实生产环境下深刻体验到高并发网络流量下 CPU 软中断的瓶颈,并且通过调整 RPS/RFS 和 中断亲和性 成功解决了问题。以下是完整的实操过程。

1. 先观察问题:软中断飙升的典型表现

第一步我用几个常用命令确认问题:

# 查看 CPU 软中断占用
mpstat -P ALL 1

# 观察系统软中断分布
cat /proc/softirqs

# 网络包接收队列负载
sar -n DEV 1

输出大概是这样的:

CPU   %usr  %sys  %soft  %iowait
0     5     8     78     0
1     3     5     80     0
2     4     7     76     0
3     5     8     77     0

%soft 居高不下,基本占据了 70%+ 的 CPU。再结合 /proc/softirqs:

NET_RX:  15893456  15678345  15467223  ...

结论:网络接收中断完全压在少数 CPU 上,ksoftirqd 在“打满”它们。

2. 理解内核网络收包机制与瓶颈

Linux 网络包接收流程简化后大致如下:

  • 网卡收到包,通过 IRQ 通知 CPU;
  • NAPI 拉取数据,触发 软中断(NET_RX);
  • 内核协议栈处理数据,最终交给用户态进程。

高并发下的典型问题:

  • 默认情况下,同一个网卡队列的中断可能固定在一个 CPU;
  • 如果网卡中断数量少(单队列或未启用多队列),所有软中断会堆在一两个 CPU 上;
  • 造成单核 ksoftirqd100% 的假象,整体吞吐反而下降。

所以解决方向是:

  • 让中断分布更均衡(中断亲和性);
  • 让包处理更分散(RPS/RFS)。

3. 调整中断亲和性(IRQ Affinity)

我首先用 cat /proc/interrupts 看中断分布:

cat /proc/interrupts | grep eth0

输出示例:

145:   567890    0    0    0  IR-PCI-MSI  eth0
146:        0    0    0    0  IR-PCI-MSI  eth0-TxRx-1

只有一个队列在处理所有流量,而且绑定在 CPU0。

我通过 irqbalance 先尝试自动均衡,但发现效果不理想,于是手动绑定:

# 查看中断号
grep eth0 /proc/interrupts

# 手动绑核,假设中断号是 145,对应 CPU 0~3
echo 0f > /proc/irq/145/smp_affinity

0f 是掩码,表示绑定 CPU0-3。

调整后软中断开始在多个 CPU 分散,但仍然有瓶颈,因为用户态进程和软中断之间还是竞争。

4. 启用 RPS(Receive Packet Steering)

RPS 是 Linux 内核的软负载均衡机制,可以把网卡接收队列的数据包分发到多个 CPU 的软中断去处理。

步骤如下:

查看网卡队列:

ls /sys/class/net/eth0/queues/
# 输出 rx-0 tx-0

配置 RPS CPU 掩码,例如让 rx-0 分发到 CPU0-3:

echo 0f > /sys/class/net/eth0/queues/rx-0/rps_cpus

调整 RPS 流表大小(提高哈希分散性):

echo 32768 > /sys/class/net/eth0/queues/rx-0/rps_flow_cnt
echo 32768 > /proc/sys/net/core/rps_sock_flow_entries

注意:rps_flow_cnt * 队列数 ≤ rps_sock_flow_entries。

配置完成后,用 htop 观察 CPU,软中断均衡了很多。

5. 启用 RFS(Receive Flow Steering)

RFS 是 RPS 的增强版,它不仅分散软中断,还会智能地将网络流量调度到运行相应应用的 CPU,减少跨核缓存抖动。

配置方法:

# 启用 RFS
echo 32768 > /proc/sys/net/core/rps_sock_flow_entries

# 每个 rx 队列配置 flow_cnt
for q in /sys/class/net/eth0/queues/rx-*; do
    echo 32768 > $q/rps_flow_cnt
done

RFS 在高并发场景下显著提升了缓存命中率,我在实测中 RTT 降低了 30% 左右。

6. 调优后的效果

再次观察:

mpstat -P ALL 1
CPU   %usr  %sys  %soft
0     10    15    25
1     11    14    24
2     9     12    23
3     10    15    26

软中断被均匀分摊,每个 CPU 只有 20%+,业务延迟明显下降。

配合 ethtool -l eth0 开启多队列,还可以进一步优化。

7. 总结最佳实践

先观察软中断和中断分布,确认瓶颈在 NET_RX;

手动绑定中断亲和性,让 IRQ 均匀落在多个 CPU;

启用 RPS/RFS,将网络包处理分散到更多核,提升缓存命中率;

合理设置 flow entries,避免默认 0 导致无效分发;

对于万兆/更高带宽网卡,结合 多队列 + RSS 效果最佳。

我通过这次真实的线上排障体会到:高并发网络问题不在用户态,而是在内核软中断调度策略。善用 RPS/RFS 和中断亲和性,可以极大提高吞吐并降低延迟。

目录结构
全文