Linux 服务器 CPU 软中断 si 偏高排查:网卡多队列、RPS 与内核参数调整

    发布时间:2026-09-23 04:31 更新时间:2026-09-23 04:31 阅读量:0

    很多站长第一次遇到 CPU 软中断问题时,场景都差不多:top 里 %Cpu(s) 的 si 一栏长期在 20% 以上,us 和 sy 都不高,业务却开始变慢,ping 的延迟抖动、Nginx 日志里出现偶发超时。更迷惑的是,整机 CPU 总使用率看着不满,但某个核已经被打满。这种情况通常和网络收包路径有关——数据包从网卡到内核协议栈的处理,很多环节都跑在软中断上下文里。

    软中断(softirq)不是传统意义上由进程调度的任务,它由内核在中断退出后、或由 ksoftirqd 内核线程择机执行。网卡每收一个包,硬中断先通知 CPU,接着把收包工作交给 NET_RX_SOFTIRQ 软中断去走协议栈。如果所有包都涌向同一个 CPU,这一个核的 si 就会飙高,其余核闲着。所以排查方向很明确:先确认软中断压在哪个核上,再让收包工作分摊到多个核。

    第一步:确认 si 偏高与中断落在哪个 CPU

    先用 top 按 1 展开每个核,观察 si 是集中在某个 CPU 还是普遍偏高。然后看网卡中断统计,/proc/interrupts 里每个中断号后面的冒号就是该中断在各 CPU 上的计数,如果某一列数字远大于其他列,说明硬中断集中在这颗核。

    # 展开每核查看 si,按 1 切换显示
     top
    
    

    查看网卡各队列中断在各 CPU 的分布

    cat /proc/interrupts | grep -i eth0

    查看软中断整体统计与每 CPU 情况

    cat /proc/softirqs | grep -i NET_RX

    如果中断计数集中在 CPU0,同时该核 si 明显偏高,基本可以判断是中断亲和性没有铺开。注意:不同发行版与网卡驱动对队列命名不同,队列名可能是 eth0-TxRx-0 这类形式,具体以实际环境的 /proc/interrupts 输出为准。

    第二步:网卡多队列与中断绑定

    现代物理网卡大多支持多队列(RSS),一块网卡可以暴露多个接收队列,每个队列绑定不同 CPU,硬件层就把流量分开了。先用 ethtool 看队列数和当前组合情况。

    # 查看网卡支持的通道数与当前配置
    ethtool -l eth0
    
    

    查看网卡特性,确认是否开启多队列相关 offload

    ethtool -k eth0 | grep -i -E 'rx|queue|rss'

    临时把接收队列调整为 4(需网卡与驱动支持)

    ethtool -L eth0 combined 4

    调整队列后,中断号会相应增加。接着可以把中断绑定到指定 CPU,写入 /proc/irq/<中断号>/smp_affinity_list 即可。绑定的原则是让每个队列落到不同核上,避开处理关键业务的那几个核。irqbalance 服务会自动做这件事,但它有时会把所有中断丢到 CPU0,所以要么配置 irqbalance 的忽略列表,要么在确定场景下手动绑定。

    # 假设 24、25、26、27 是 eth0 的四个队列中断号
    for i in 24 25 26 27; do
      echo $((i-24+2)) > /proc/irq/$i/smp_affinity_list
    done
    
    

    查看绑定结果

    cat /proc/irq/24/smp_affinity_list

    上面的写法把四个队列分别绑到 CPU2 到 CPU5,只是示例,实际用哪个核要结合业务进程分布来定。写入 /proc 的配置重启即失效,需要持久化时可通过 systemd 单元或 rc.local 在启动阶段执行,具体方式以发行版文档为准。

    第三步:RPS 让软件层分摊收包

    如果网卡队列数少于 CPU 核数,或者用的是虚拟网卡(很多云主机的 virtio 网卡队列有限),硬件层分摊不开,就要靠 RPS(Receive Packet Steering)。RPS 在软件层根据数据包的哈希值,把收包软中断分派到不同 CPU,相当于用 CPU 换队列数。

    开启 RPS 是往 /sys/class/net/<网卡>/queues/rx-<n>/rps_cpus 写一个十六进制掩码。掩码的每一位对应一个 CPU,比如 CPU0 到 CPU3 对应掩码的 0 到 3 位,写 f 就表示这四颗核都参与。

    # 查看当前 RPS 配置(0 表示未开启)
    cat /sys/class/net/eth0/queues/rx-0/rps_cpus
    
    

    让 CPU0-3 参与 RPS,掩码为 f

    for q in /sys/class/net/eth0/queues/rx-*; do echo f > $q/rps_cpus done

    调整 RPS 流表大小,默认 4096,可按需增大

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

    RPS 生效后,/proc/softirqs 里 NET_RX 在各 CPU 上的计数会明显均衡,si 也会从单核转移到多核。需要注意的是,RPS 只影响接收方向,发送方向对应的是 XPS;掩码写错(例如写了不存在的 CPU 位)不会报错但也不生效,改完记得回读确认。

    第四步:几个配套的内核参数

    收包路径上还有几个内核参数会影响软中断压力。netdev_max_backlog 是每个 CPU 的收包队列长度,小包高并发场景下队列容易满并丢包,日志里会出现 dropped;somaxconn 影响 accept 队列;tcp_max_syn_backlog 影响半连接队列。这些值不是越大越好,盲目调大只是把丢包点后移,要结合 ss -lnt 的 Send-Q 和 Recv-Q 观察。

    # 查看当前值与丢包统计
    sysctl net.core.netdev_max_backlog
    cat /proc/net/softnet_stat
    
    

    临时调整(写入 /etc/sysctl.d/ 下文件可持久化)

    sysctl -w net.core.netdev_max_backlog=8192 sysctl -w net.core.somaxconn=4096 sysctl -w net.ipv4.tcp_max_syn_backlog=8192

    使配置文件生效

    sysctl --system

    /proc/net/softnet_stat 每行对应一个 CPU,第一列是处理的包数,第二列是因队列满被丢弃的包数。如果某颗核的第二列持续增长,说明该核的 backlog 不够或负载不均,优先解决分布问题而不是一味加大数值。

    最后提醒两点:一是调整队列、RPS 与中断绑定前,建议记录原始值,便于回退;二是云主机、容器环境下的网卡能力往往受限,虚拟网卡可能不支持多队列或 RPS 效果有限,实际可用能力以云厂商与内核版本为准。排查完这一轮,如果 si 仍然偏高,再往协议栈上层看,比如 conntrack 表满、iptables 规则过多导致每个包都要走一遍匹配,也会把软中断顶上去。

    继续阅读

    📑 📅
    Docker 容器启动即退出排查:exit code、logs 与前台进程 2026-09-23
    MySQL单表过亿后的分页优化:延迟关联与覆盖索引 2026-09-23
    Nginx 反代下 Cookie 域与 Path 错乱排查实战 2026-09-23
    Linux 改完 fstab 重启起不来:UUID 混用与救援恢复 2026-09-22
    MySQL备份文件损坏怎么验证:一致性校验与恢复演练 2026-09-22
    Nginx客户端与后端长连接谁在复用:端口耗尽排查顺序 2026-09-23
    Linux conntrack 表满导致丢包:现场定位与容量评估 2026-09-23
    rsyslog 与 journald 双写日志重复或丢失排查 2026-09-23
    PostgreSQL表膨胀与autovacuum不生效排查实战 2026-09-23
    Redis 主从切换后写入报 READONLY:三种拓扑的误配排查 2026-09-23