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 中断亲和性配置流程

下图展示了从用户态配置到中断控制器路由的完整流程:

中断亲和性配置与生效流程 用户态写入 echo 0-3 > /proc/irq/N/smp_affinity_list 内核IRQ子系统 更新irq_desc->affinity 中断控制器 路由表更新 硬件中断到达 目标CPU核心 关键说明 • smp_affinity 和 smp_affinity_list 是用户态接口 • 内核IRQ子系统负责将位掩码转换为中断控制器指令 • 中断控制器(如IOAPIC)根据路由表将中断送达指定CPU • effective_affinity 反映硬件实际支持的限制

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:

  1. 查找网卡中断号:
    $ 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
  2. 停止irqbalance:
    $ systemctl stop irqbalance
  3. 设置亲和性(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
  4. 验证中断分布:
    $ 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。