3. 中断机制与调优:理解MSI-X、中断亲和性(IRQ Affinity)及中断合并(Coalescing)

网卡中断,说白了就是网卡对CPU说:“嘿,有数据来了,快来处理!”

但问题是——如果网卡每来一个包就喊一次CPU,那CPU就别干别的了。我见过不少线上事故,都是中断处理不当导致的CPU软中断飙到100%,业务直接卡死。

所以,理解中断机制,是网卡调优的必修课。今天咱们就聊聊MSI-X、中断亲和性、中断合并这三板斧。

3.1 中断的演进:从IRQ到MSI-X

早期的网卡用传统IRQ(中断请求线)。每个设备固定占用一条中断线,CPU收到中断后,得挨个查是哪个设备触发的。效率低,还容易冲突。

后来有了MSI(Message Signaled Interrupt),设备直接往CPU写一个特殊内存地址来触发中断。省去了中断线,但每个设备还是只能用一个中断向量。

MSI-X 是MSI的升级版。它允许一个设备拥有多达2048个独立的中断向量。每个队列可以绑定一个独立的中断。

核心价值:多队列网卡 + MSI-X = 每个队列一个中断,每个中断可以绑定到不同CPU核心。这才是现代高性能网卡的基石。

我调过一台服务器,网卡支持64个队列,但BIOS里MSI-X没开。结果所有中断挤在一个CPU上,软中断占用100%,其他核心闲得发慌。开了MSI-X之后,吞吐量直接翻倍。

3.2 中断亲和性(IRQ Affinity)

中断亲和性,就是指定某个中断由哪个CPU核心来处理。

为什么要做这个?因为CPU缓存有亲和性。如果一个中断总是在同一个核心上处理,那这个核心的L1/L2缓存里很可能还留着上次处理的数据。换到另一个核心,缓存就全废了。

3.2.1 查看当前中断亲和性

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

# 查看某个中断的亲和性设置(以中断号78为例)
cat /proc/irq/78/smp_affinity

输出是一个十六进制数。比如 00000001 表示只绑定到CPU 0,00000003 表示绑定到CPU 0和CPU 1。

3.2.2 设置中断亲和性

# 将中断78绑定到CPU 2(二进制0100,十六进制04)
echo 04 > /proc/irq/78/smp_affinity

我的习惯:smp_affinity_list 更直观,直接写CPU编号。

echo 2 > /proc/irq/78/smp_affinity_list

3.2.3 自动设置工具:irqbalance

手动配中断亲和性太累,尤其机器多的时候。irqbalance 这个守护进程可以自动分配中断到各个CPU核心。

# 安装
yum install irqbalance -y

# 启动
systemctl start irqbalance
systemctl enable irqbalance

但注意,irqbalance 默认策略是“功耗均衡”,不是“性能最大化”。

我曾经踩过的坑:线上数据库服务器开了irqbalance,结果网卡中断在多个核心间频繁迁移,导致CPU缓存命中率暴跌,延迟从50μs飙到200μs。后来我关了irqbalance,手动把网卡中断绑定到两个专用的核心上,问题解决。

所以,延迟敏感的场景,建议手动绑定。吞吐量场景,irqbalance 够用。

3.3 中断合并(Coalescing)

中断合并,就是让网卡攒一批包再通知CPU。说白了,用延迟换吞吐。

你想想看,如果每个小包都触发中断,CPU频繁上下文切换,吞吐量上不去。但攒多了再通知,延迟就上去了。

3.3.1 查看当前中断合并参数

ethtool -c eth0

输出示例:

Coalesce parameters for eth0:
Adaptive RX: on  TX: on
rx-usecs: 100
rx-frames: 256
tx-usecs: 100
tx-frames: 256

关键参数解释:

参数 含义
rx-usecs 接收方向,最多等多少微秒再发中断
rx-frames 接收方向,攒够多少个帧再发中断
Adaptive RX/TX 自适应模式,网卡自己根据流量动态调整

3.3.2 调优建议

我个人习惯这样调:

  • 延迟敏感场景(如Redis、MySQL):关掉自适应,设 rx-usecs=1,rx-frames=1。每个包立即中断,延迟最低。
  • 吞吐量场景(如文件传输、视频流):开自适应,或者手动设 rx-usecs=50~100,rx-frames=256~512。
  • 混合场景:开自适应,让网卡自己判断。
# 关闭自适应,手动设置低延迟
ethtool -C eth0 adaptive-rx off adaptive-tx off
ethtool -C eth0 rx-usecs 1 rx-frames 1

# 开启自适应
ethtool -C eth0 adaptive-rx on adaptive-tx on

注意:不是所有网卡都支持自适应模式。老款网卡可能只有固定参数。改之前先查一下网卡型号和驱动版本。

3.4 核心逻辑:中断调优的决策树

下面这张图,是我自己总结的中断调优决策流程。每次调优前,我都会过一遍这个逻辑。

中断调优决策树 开始调优 延迟敏感? 低延迟配置 rx-usecs=1 吞吐量优先? 吞吐量配置 自适应开启 混合场景 自适应开启 多队列? 手动绑定 每个队列绑不同CPU irqbalance 自动分配 验证效果

3.5 实战:一键调优脚本

最后,分享一个我常用的调优脚本片段。它做了三件事:关irqbalance、开MSI-X、手动绑定中断到前4个核心。

#!/bin/bash
# 网卡中断调优脚本

NIC="eth0"

# 1. 关闭 irqbalance
systemctl stop irqbalance
systemctl disable irqbalance

# 2. 确认 MSI-X 已启用
ethtool -i $NIC | grep -i "msi-x"
if [ $? -ne 0 ]; then
    echo "警告:MSI-X 未启用,请检查驱动参数"
fi

# 3. 获取网卡中断号
IRQS=$(cat /proc/interrupts | grep $NIC | awk -F: '{print $1}' | tr -d ' ')

# 4. 绑定到 CPU 0-3
CPU=0
for irq in $IRQS; do
    echo $CPU > /proc/irq/$irq/smp_affinity_list
    CPU=$(( (CPU + 1) % 4 ))
done

echo "调优完成!"

验证方法:跑完脚本后,用 cat /proc/interrupts | grep eth0 看看中断是不是分散到了不同CPU。如果所有中断还在CPU0上,说明哪里没配对。

嗯,中断调优这块,说白了就是让网卡和CPU之间的配合更默契。MSI-X解决“能不能分开”的问题,中断亲和性解决“分给谁”的问题,中断合并解决“什么时候给”的问题。三管齐下,网卡性能才能发挥到极致。


公众号:蓝海资料掘金营,微信deep3321