第3章:Linux中断子系统:中断描述符、中断控制器、中断处理流程
说实话,搞DMA中断亲和性调优,第一步不是看CPU掩码怎么设,而是把中断子系统的基本功打扎实。你想想,连中断怎么来、怎么走、怎么分发都不清楚,就想上手写亲和性配置,那基本是盲人摸象。
这一章,我带你把这几个核心概念掰扯清楚。
3.1 中断描述符:irq_desc 与 irq_chip
每个中断在Linux内核里都对应一个 irq_desc 结构体。这个结构体是中断的「身份证」——记录了中断号、触发方式、状态、处理函数、亲和性掩码等等。
我在项目里调试网卡性能时,经常直接 cat /proc/interrupts 看每个中断的分布,其实背后就是遍历这些描述符。
irq_desc 关键字段
| 字段名 | 作用 | 关联配置 |
|---|---|---|
irq_data |
芯片级中断数据 | 关联到中断控制器 |
action |
中断处理函数链表 | 注册/注销中断 |
affinity_hint |
亲和性提示掩码 | 驱动/用户态设置 |
depth |
中断禁用嵌套深度 | 调试时排查禁用问题 |
istate |
中断状态标志 | IRQ_WAKE_THREAD等 |
irq_domain 的映射关系。
irq_chip 结构体是中断控制器的「驱动接口」。它定义了怎么屏蔽、怎么使能、怎么设置触发方式。说白了,它是一套回调函数集合,让上层代码不用关心底层芯片差异。
3.2 中断控制器:从PIC到GIC
传统PC上大家熟知的8259A PIC,只能管15个中断。现在多核ARM里主流的GIC,能管上千个,还能做到中断分发到指定CPU。
我个人觉得,理解中断控制器的关键是搞清楚两件事:
- 它怎么从设备收中断?
- 它怎么把中断发到CPU?
拿GIC-400举例,它分为分发器(Distributor)和CPU接口(CPU Interface)两部分。分发器负责收集中断、配置优先级;CPU接口负责把中断送给某个核。
| 中断控制器类型 | 典型平台 | 核心机制 | 亲和性支持 |
|---|---|---|---|
| 8259A PIC | x86单核/早期多核 | 级联、固定优先级 | 不支持 |
| APIC (x2APIC) | x86多核 | IOAPIC+LAPIC、MSI | 通过IOREDTBL配置 |
| GIC-400 | ARM Cortex-A系列 | 分发器+CPU接口 | 通过GICD_ITARGETSR |
| GICv3/v4 | ARMv8服务器 | ITS、LPI、Direct Injection | 通过ITS路由表 |
/proc/interrupts 里看到的那个irq号,和硬件中断号之间隔着一层映射。这个映射就是 irq_domain 在做的事。调试时别忘了 cat /proc/irq/{irq_number}/irq_domain 看看。
3.3 中断处理流程:从硬件到软件
设备触发中断,到你的中断处理函数被执行,中间经过了好几层。我通常把这个流程拆成五个步骤:
- 硬件触发: 设备拉高中断引脚,或者发送MSI消息。
- 中断控制器接收: GIC或APIC收到信号,查表找到对应的中断号。
- irq_domain映射: 把硬件中断号转换成Linux的irq号。
- irq_desc查找: 根据irq号找到对应的中断描述符。
- 处理函数执行: 调用
action->handler。
这里有个容易被忽略的细节:中断处理上下文分上半部和下半部。上半部(primary handler)在中断上下文里执行,要求快进快出;下半部(threaded handler)可以放在线程里跑。
重点来了: 我们配置中断亲和性,其实是在影响第3步到第5步之间的一个关键环节——中断分布策略。你通过 /proc/irq/{irq}/smp_affinity 设置亲和性掩码,底层中断控制器就会根据这个掩码,决定把中断送到哪个CPU核。
我前两天才帮同事调了一个网卡中断分布不均的问题。八个CPU核,中断全挤在核0上。查看 /proc/irq/77/smp_affinity 发现是默认掩码 ff,但GIC没做自动负载均衡——因为有些平台默认把中断都发到核0。手动改成 0f,只让前四个核处理,效果立竿见影。
嗯,这就引出了下一件事:你要知道你的中断控制器是否支持自动均衡。GICv2依赖软件配置,GICv3/v4可以通过ITS实现硬件分发。搞清楚这个,你才知道问题出在配置上还是硬件能力上。
smp_affinity 就万事大吉。有些平台的BIOS或bootloader会覆盖你的设置。我就在某款ARM服务器上遇到过——内核启动后中断亲和性被写死,手动改 /proc/irq/*/smp_affinity 根本改不了,后来查代码才发现是固件锁了寄存器。
总结一下这一章的核心:中断描述符是每个中断的实体,中断控制器是物理通道,irq_domain是中间桥梁。你调优亲和性,本质上是在跟这三者打交道。后面章节里,我会具体讲怎么在不同控制器上配置亲和性、怎么通过procfs调试、以及怎么用代码实时调整。