3. CPU中断亲和性(SMP IRQ Affinity):/proc/irq/目录结构、smp_affinity与smp_affinity_list文件解析
在多核(SMP)系统中,中断控制器(如APIC)可以将外设中断路由到指定的CPU核心处理。通过配置中断亲和性(IRQ Affinity),系统管理员可以精细控制“哪个CPU处理哪个中断”,从而避免单核过载、提升缓存命中率并降低尾延迟。
核心概念: 中断亲和性本质上是一个CPU位掩码(bitmask),每一位代表一个逻辑CPU。置1的位表示该中断可以被对应的CPU处理。
3.1 /proc/irq/目录结构
Linux内核通过/proc/irq/伪文件系统暴露每个中断号(IRQ)的运行时配置。每个已注册的中断号对应一个子目录,目录名即为中断号。
$ ls /proc/irq/
0 1 8 9 12 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 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
每个IRQ子目录下包含以下关键文件:
| 文件/目录 | 类型 | 说明 |
|---|---|---|
smp_affinity |
文件 | 十六进制位掩码,表示允许处理该中断的CPU集合 |
smp_affinity_list |
文件 | CPU列表格式(如0-3,5),与smp_affinity等价但更易读 |
effective_affinity |
文件 | 内核实际生效的CPU位掩码(考虑中断控制器限制) |
node |
文件 | 该中断关联的NUMA节点编号 |
actions |
目录 | 注册在该中断号上的设备驱动名称列表 |
实践检查: 查看网卡中断(通常为高编号IRQ)的当前亲和性配置:
# 查看eth0对应的IRQ号
$ cat /proc/interrupts | grep eth0
46: 12345 0 0 0 IR-PCI-MSI eth0
# 查看该IRQ的亲和性
$ cat /proc/irq/46/smp_affinity
00000000,00000000,00000000,00000001
上述输出表示中断46仅允许CPU0处理(位掩码最低位为1)。
3.2 smp_affinity文件解析
smp_affinity使用十六进制位掩码表示CPU集合。每个十六进制数字对应4个CPU位,从最低位(CPU0)开始排列。
位掩码与CPU的对应关系:
| 位掩码(十六进制) | 二进制表示 | 允许的CPU |
|---|---|---|
1 |
0001 | CPU0 |
2 |
0010 | CPU1 |
3 |
0011 | CPU0, CPU1 |
f |
1111 | CPU0~CPU3 |
ff |
11111111 | CPU0~CPU7 |
00000000,00000000,00000000,0000000f |
低32位全1 | CPU0~CPU31 |
注意: 对于超过32个CPU的系统,
smp_affinity会以逗号分隔的多个32位组呈现。例如64位系统上允许所有CPU的掩码为:ffffffff,ffffffff。写入时必须保持相同的分组格式。
3.3 smp_affinity_list文件解析
smp_affinity_list提供更直观的CPU列表格式,适合人类阅读和脚本处理。它与smp_affinity完全等价,修改任一文件都会同步更新另一个。
格式规则:
- 单个CPU:直接写CPU编号,如
0 - 连续范围:使用短横线连接起止编号,如
0-3 - 不连续集合:使用逗号分隔,如
0,2,4 - 混合格式:如
0-2,4,6-7
# 查看当前配置
$ cat /proc/irq/46/smp_affinity_list
0
# 修改为允许CPU0~CPU3处理
$ echo 0-3 > /proc/irq/46/smp_affinity_list
# 验证修改
$ cat /proc/irq/46/smp_affinity
00000000,00000000,00000000,0000000f
$ cat /proc/irq/46/smp_affinity_list
0-3
3.4 中断亲和性配置流程
下图展示了从用户态配置到中断控制器路由的完整流程:
3.5 配置生效验证与常见问题
验证配置是否生效:
# 方法1:查看中断分布
$ watch -d -n 1 'cat /proc/interrupts | grep eth0'
# 方法2:检查effective_affinity
$ cat /proc/irq/46/effective_affinity
00000000,00000000,00000000,0000000f
# 方法3:查看每个CPU的中断计数
$ cat /proc/interrupts | head -1
CPU0 CPU1 CPU2 CPU3
46: 1234 5678 9012 3456 IR-PCI-MSI eth0
常见陷阱:
- 中断控制器限制:某些旧式中断控制器不支持任意CPU路由,此时
effective_affinity会显示实际可用的CPU子集。 - MSI/MSI-X中断:PCIe MSI-X中断每个队列可以独立设置亲和性,但需要通过
/sys/class/net/eth0/device/msi_irqs/下的单独文件配置。 - IRQ平衡守护进程:如果
irqbalance服务正在运行,它会定期覆盖手动设置的亲和性。生产环境建议在配置前停止该服务:systemctl stop irqbalance。
3.6 实战:将网卡中断绑定到指定CPU
以下步骤演示如何将eth0的所有RX队列中断绑定到CPU2和CPU3:
- 查找网卡中断号:
$ cat /proc/interrupts | grep eth0 46: 12345 0 0 0 IR-PCI-MSI eth0-rx-0 47: 0 12345 0 0 IR-PCI-MSI eth0-rx-1 48: 0 0 12345 0 IR-PCI-MSI eth0-rx-2 49: 0 0 0 12345 IR-PCI-MSI eth0-rx-3 - 停止irqbalance:
$ systemctl stop irqbalance - 设置亲和性(CPU2和CPU3对应位掩码0xC):
$ echo 0xC > /proc/irq/46/smp_affinity $ echo 0xC > /proc/irq/47/smp_affinity $ echo 0xC > /proc/irq/48/smp_affinity $ echo 0xC > /proc/irq/49/smp_affinity - 验证中断分布:
$ cat /proc/interrupts | grep eth0 46: 0 0 12345 0 IR-PCI-MSI eth0-rx-0 47: 0 0 0 12345 IR-PCI-MSI eth0-rx-1 48: 0 0 12345 0 IR-PCI-MSI eth0-rx-2 49: 0 0 0 12345 IR-PCI-MSI eth0-rx-3可以看到所有中断现在仅在CPU2和CPU3之间分布。
性能收益: 将网卡中断绑定到特定CPU核(通常与应用程序所在核分离),可以避免中断处理抢占应用CPU时间,同时利用CPU本地缓存提升处理效率。在DPDK等高吞吐场景中,通常将中断绑定到非数据面CPU。