4. 中断亲和性(IRQ Affinity)
中断亲和性(IRQ Affinity)是 Linux 内核提供的一种机制,允许系统管理员将特定的硬件中断(IRQ)绑定到指定的 CPU 核心上处理。通过合理配置中断亲和性,可以显著提升多核 CPU 系统的网络处理性能,避免单个 CPU 成为瓶颈。
4.1 核心概念
在默认情况下,中断可以在所有 CPU 核心之间动态分配。这种灵活性虽然能平衡负载,但会带来以下问题:
- CPU 缓存污染:中断在不同核心间迁移,导致 L1/L2 缓存频繁失效。
- 锁竞争加剧:多个核心同时处理同一设备的中断,增加自旋锁开销。
- 延迟不可预测:中断响应时间因核心负载不同而波动。
中断亲和性通过设置 CPU 位掩码(bitmask),将中断固定到特定核心,从而:
- 提高缓存命中率,减少内存访问延迟。
- 降低锁竞争,提升多核扩展性。
- 使中断处理延迟更稳定、可预测。
4.2 smp_affinity 文件
在 Linux 系统中,每个中断的亲和性设置通过 /proc/irq/{IRQ_NUM}/smp_affinity 文件进行管理。该文件存储一个十六进制位掩码,每一位代表一个 CPU 核心。
| 文件路径 | 说明 |
|---|---|
/proc/irq/{IRQ_NUM}/smp_affinity |
存储中断亲和性的十六进制位掩码(如 02 表示仅 CPU 1) |
/proc/irq/{IRQ_NUM}/smp_affinity_list |
以 CPU 列表形式显示亲和性(如 1 表示 CPU 1) |
/proc/interrupts |
显示每个中断在每个 CPU 上的处理次数统计 |
位掩码计算规则
位掩码从最低位(bit 0)开始对应 CPU 0,bit 1 对应 CPU 1,以此类推。常用掩码值如下:
| 目标 CPU | 二进制位掩码 | 十六进制值 |
|---|---|---|
| CPU 0 | 0000 0001 | 01 |
| CPU 1 | 0000 0010 | 02 |
| CPU 2 | 0000 0100 | 04 |
| CPU 3 | 0000 1000 | 08 |
| CPU 0-3(全部) | 0000 1111 | 0f |
| CPU 0 和 CPU 2 | 0000 0101 | 05 |
cat /sys/class/net/eth0/device/numa_node 查看网卡所在的 NUMA 节点。
4.3 手动绑定中断到指定 CPU
手动绑定中断亲和性分为三个步骤:查找中断号、计算位掩码、写入配置文件。以下以绑定网卡 eth0 的接收队列中断到 CPU 1 为例。
步骤 1:查找中断号
通过 /proc/interrupts 查看网卡队列对应的中断号:
# cat /proc/interrupts | grep eth0
CPU0 CPU1 CPU2 CPU3
24: 12345 0 0 0 IR-PCI-MSI eth0-rx-0
25: 0 23456 0 0 IR-PCI-MSI eth0-tx-0
26: 0 0 34567 0 IR-PCI-MSI eth0-rx-1
27: 0 0 0 45678 IR-PCI-MSI eth0-tx-1
从输出可知,eth0-rx-0(接收队列 0)的中断号为 24。
步骤 2:计算位掩码
要将中断绑定到 CPU 1(核心编号从 0 开始),位掩码为 02(二进制 0000 0010)。
步骤 3:写入 smp_affinity 文件
# echo 02 > /proc/irq/24/smp_affinity
验证绑定是否生效:
# cat /proc/irq/24/smp_affinity
02
# cat /proc/irq/24/smp_affinity_list
1
# cat /proc/interrupts | grep "24:"
CPU0 CPU1 CPU2 CPU3
24: 12345 23456 0 0 IR-PCI-MSI eth0-rx-0
可以看到,中断 24 现在仅在 CPU 0 和 CPU 1 上处理(写入 02 后,新中断仅由 CPU 1 处理,但历史计数仍保留在 CPU 0 上)。
4.4 持久化配置
手动写入 /proc/irq/{IRQ_NUM}/smp_affinity 的设置在系统重启后会丢失。要实现持久化,有以下两种常用方法:
| 方法 | 配置方式 | 适用场景 |
|---|---|---|
| irqbalance 服务 | 配置 /etc/sysconfig/irqbalance 或 /etc/default/irqbalance,设置 IRQBALANCE_BANNED_CPUS 排除特定核心 |
需要动态平衡,但希望保留部分核心专用于中断 |
| rc.local / systemd 服务 | 在系统启动脚本中执行 echo 命令写入 smp_affinity |
需要精确控制每个中断的绑定目标 |
| udev 规则 | 通过 /etc/udev/rules.d/ 中的规则,在设备加载时自动设置亲和性 |
设备热插拔场景,如 SR-IOV VF 绑定 |
systemctl stop irqbalance),或通过 IRQBALANCE_BANNED_CPUS 将目标 CPU 排除在动态平衡范围之外。
4.5 多队列网卡的批量绑定
对于多队列网卡(如 ixgbe、mlx5 等),通常每个接收/发送队列对应一个独立的中断号。批量绑定可以按照队列编号与 CPU 核心一一对应的策略进行:
#!/bin/bash
# 批量绑定网卡 eth0 的接收队列中断到不同 CPU
# 假设有 4 个接收队列,绑定到 CPU 0-3
for irq in $(grep "eth0-rx" /proc/interrupts | awk '{print $1}' | sed 's/://'); do
cpu=$((irq % 4)) # 根据中断号取模分配 CPU
mask=$(printf '%x' $((1 << cpu)))
echo $mask > /proc/irq/$irq/smp_affinity
echo "IRQ $irq → CPU $cpu (mask: $mask)"
done
执行结果示例:
IRQ 24 → CPU 0 (mask: 1)
IRQ 25 → CPU 1 (mask: 2)
IRQ 26 → CPU 2 (mask: 4)
IRQ 27 → CPU 3 (mask: 8)
4.6 验证与监控
绑定完成后,可以通过以下命令验证中断分布是否符合预期:
# 查看中断在各 CPU 上的分布
watch -n 1 'cat /proc/interrupts | grep eth0'
# 查看特定中断的亲和性设置
cat /proc/irq/24/smp_affinity
cat /proc/irq/24/smp_affinity_list
# 查看 CPU 的中断处理负载
mpstat -I CPU 1 5
通过 mpstat -I 可以查看每个 CPU 的中断处理时间占比,确保绑定后中断负载集中在目标核心上,而其他核心的中断处理时间趋近于零。