3、SMP与中断亲和性:对称多处理架构、CPU亲和性概念、中断亲和性(IRQ Affinity)原理
3.1 对称多处理(SMP)架构概述
对称多处理(Symmetric Multi-Processing,SMP)是现代服务器与桌面系统中最常见的多处理器架构。在SMP系统中,两个或更多相同的处理器通过共享总线连接到统一的内存子系统,每个处理器对所有内存和设备具有同等的访问权限。
| 特性 | 描述 | 对中断的影响 |
|---|---|---|
| 共享内存 | 所有CPU通过统一地址空间访问物理内存 | 中断处理函数可访问任意内存位置,无需数据迁移 |
| 对等调度 | 调度器可将任务分配给任意空闲CPU | 中断也可灵活绑定到不同CPU,实现负载分散 |
| 缓存一致性 | 硬件维护各CPU缓存间的数据一致性(MESI协议) | 中断亲和性设置需考虑缓存局部性,避免跨CPU缓存失效 |
| 总线竞争 | 多个CPU共享内存总线与I/O总线 | 不合理的中断分布可能导致总线热点,降低整体吞吐 |
3.2 CPU亲和性(CPU Affinity)概念
CPU亲和性(CPU Affinity)是指将进程或线程绑定到特定CPU核心(或核心集合)上运行的机制。在Linux中,亲和性通过CPU位掩码(cpumask)表示,每一位对应一个逻辑CPU。
· 软亲和性(Soft Affinity): 调度器尽量将进程保持在同一个CPU上运行,但不强制。由内核调度策略自动实现。
· 硬亲和性(Hard Affinity): 通过系统调用或cgroup强制进程只能在指定的CPU集合上运行。中断亲和性属于硬亲和性。
3.2.1 CPU亲和性的表示方法
Linux使用位掩码(bitmask)表示CPU集合。每个CPU对应掩码中的一位,从最低位(bit 0)开始编号。
| CPU集合 | 二进制掩码 | 十六进制表示 | 说明 |
|---|---|---|---|
| 仅CPU 0 | 0000 0001 | 0x01 | 仅绑定到第一个核心 |
| CPU 0-1 | 0000 0011 | 0x03 | 绑定到前两个核心 |
| CPU 2-3 | 0000 1100 | 0x0C | 绑定到第3、4个核心 |
| 所有CPU(4核) | 0000 1111 | 0x0F | 允许在所有核心上运行 |
| CPU 0,2 | 0000 0101 | 0x05 | 绑定到第1、3个核心 |
3.2.2 进程亲和性操作命令
# 查看进程的当前CPU亲和性
taskset -p <PID>
# 将进程绑定到CPU 0和CPU 2(掩码0x05)
taskset -p 0x05 <PID>
# 启动进程时直接指定亲和性
taskset -c 0,2 ./myapp
# 使用cgroup限制CPU集合
mkdir /sys/fs/cgroup/cpuset/mygroup
echo "0-2" > /sys/fs/cgroup/cpuset/mygroup/cpuset.cpus
echo <PID> > /sys/fs/cgroup/cpuset/mygroup/cgroup.procs
3.3 中断亲和性(IRQ Affinity)原理
中断亲和性(IRQ Affinity)是CPU亲和性概念在中断处理中的具体应用。它允许系统管理员或内核将特定的硬件中断请求(IRQ)定向到指定的CPU核心进行处理,而不是让所有中断都落在同一个CPU上。
3.3.1 中断亲和性的工作原理
中断亲和性的实现依赖于现代中断控制器——高级可编程中断控制器(APIC)和I/O APIC。每个中断源(如网卡、磁盘控制器)在I/O APIC中都有一个对应的重定向表项(Redirection Table Entry),该表项中包含目标CPU掩码字段。
- 设备触发中断: 硬件设备向I/O APIC发送中断信号。
- I/O APIC查表: I/O APIC根据中断号查找重定向表项,读取目标CPU掩码。
- 消息发送: I/O APIC将中断消息发送到目标CPU的本地APIC(Local APIC)。
- CPU响应: 目标CPU的本地APIC接收中断,触发CPU执行中断处理程序。
3.3.2 中断亲和性的配置接口
Linux通过/proc/irq/<IRQ_NUM>/smp_affinity和/proc/irq/<IRQ_NUM>/smp_affinity_list两个文件暴露中断亲和性的配置接口。
# 查看IRQ 78的当前亲和性(十六进制掩码)
cat /proc/irq/78/smp_affinity
# 输出示例:00000000,0000000f (表示CPU 0-3)
# 查看IRQ 78的亲和性(CPU列表格式)
cat /proc/irq/78/smp_affinity_list
# 输出示例:0-3
# 将IRQ 78绑定到CPU 0和CPU 2(掩码0x05)
echo 05 > /proc/irq/78/smp_affinity
# 将IRQ 78绑定到CPU 1-2(列表格式)
echo 1-2 > /proc/irq/78/smp_affinity_list
smp_affinity时,掩码中的位必须对应系统中实际存在的CPU。如果写入的CPU不在线(offline),内核会静默忽略该位。此外,某些中断(如时钟中断、IPI)的亲和性由内核内部管理,不建议手动修改。
3.3.3 中断亲和性与缓存局部性
中断亲和性的一个关键优化目标是缓存局部性(Cache Locality)。当网络数据包到达时,如果中断始终由同一个CPU处理,该CPU的L1/L2缓存中会保留频繁访问的数据结构(如网络协议栈的sk_buff、socket结构),从而减少缓存未命中(Cache Miss)次数。
| 场景 | 中断亲和性设置 | 缓存效果 | 性能影响 |
|---|---|---|---|
| 单队列网卡 | 绑定到1个CPU | 缓存命中率高,数据局部性好 | 适合低吞吐场景,CPU利用率集中 |
| 多队列网卡(RSS) | 每个队列绑定不同CPU | 每个CPU独立缓存,避免竞争 | 高吞吐场景下线性扩展 |
| 中断频繁切换CPU | 无固定亲和性(默认) | 缓存频繁失效,跨CPU数据迁移 | 性能下降20%-50% |
| NUMA节点内绑定 | 绑定到同NUMA节点的CPU | 本地内存访问,避免跨节点延迟 | 延迟降低30%-60% |
3.3.4 查看当前中断分布
使用/proc/interrupts文件可以查看每个中断在各个CPU上的处理次数,这是评估中断亲和性设置效果的最直接手段。
# 查看中断在各CPU上的分布
cat /proc/interrupts
# 输出示例(简化):
# CPU0 CPU1 CPU2 CPU3
# 78: 12345678 23456789 34567890 45678901 IR-PCI-MSI eth0
# 79: 0 0 98765432 0 IR-PCI-MSI eth1
# 分析:
# - IRQ 78(eth0)在4个CPU上均匀分布,说明RSS或irqbalance生效
# - IRQ 79(eth1)仅由CPU2处理,可能是手动绑定的结果
/proc/interrupts时,如果发现某个CPU的中断计数远高于其他CPU(尤其是CPU 0),说明中断负载不均衡,应考虑调整亲和性或启用irqbalance。理想情况下,高吞吐设备的中断应均匀分布在所有CPU上,同时保持每个中断流固定在一个CPU上。
3.4 中断亲和性与NUMA架构的交互
在NUMA(Non-Uniform Memory Access)系统中,中断亲和性的设置需要额外考虑NUMA节点拓扑。将中断绑定到与设备所在NUMA节点相同的CPU上,可以避免跨节点内存访问带来的额外延迟。
# 查看NUMA节点拓扑
numactl --hardware
# 查看设备所在的NUMA节点(以网卡为例)
cat /sys/class/net/eth0/device/numa_node
# 输出:0 (表示设备在NUMA节点0)
# 将中断绑定到同NUMA节点的CPU(节点0的CPU通常为0-3)
echo 0f > /proc/irq/78/smp_affinity
中断亲和性是Linux系统性能调优的核心技术之一。正确配置中断亲和性,可以显著提升网络吞吐、降低延迟、避免CPU热点,为后续学习irqbalance的自动均衡策略奠定基础。