14、中断子系统结构体抽象
在Linux系统中,设备通过IRQ(中断请求)向系统发送信号,告诉CPU有事情需要处理。当设备需要CPU帮忙时,就会通过IRQ线发送一个中断信号。
CPU接到信号后,会暂停当前工作,先把正在进行的任务状态保存起来,然后去处理设备请求。处理完设备的事情后,再恢复之前保存的任务状态,继续之前的工作。
因为设备太多,IRQ线的数量也很多,CPU直接管理所有中断会很麻烦。所以系统会用一个专门的设备(比如中断控制器)来帮CPU管理这些IRQ线,这样CPU就能更高效地处理任务了。
struct irq_chip
:直接操作中断控制器的硬件,比如控制中断的开启、关闭或触发等底层功能。struct irq_domain
:负责把硬件设备产生的物理中断号,转换成Linux系统内部统一管理的中断号,就像给中断信号做翻译一样。struct irq_desc
:记录每个中断的详细信息,比如中断是否被屏蔽、处理函数是什么,相当于中断的"档案卡片"。
一、中断控制器:irq_chip
中断控制器在内核中表示为struct irq_chip
结构的实例,它描述实际硬件设备,以及IRQ
内核使用的一些方法:
struct irq_chip {
struct device *parent_device;
const char *name;
void (*irq_enable)(struct irq_data *data);
void (*irq_disable)(struct irq_data *data);
void (*irq_ack)(struct irq_data *data);
void (*irq_mask)(struct irq_data *data);
void (*irq_unmask)(struct irq_data *data);
void (*irq_eoi)(struct irq_data *data);
int (*irq_set_affinity)(struct irq_data *data, const struct cpumask *dest, bool force);
int (*irq_retrigger)(struct irq_data *data);
int (*irq_set_type)(struct irq_data *data, unsigned int flow_type);
int (*irq_set_wake)(struct irq_data *data, unsigned int on);
void (*irq_bus_lock)(struct irq_data *data);
void (*irq_bus_sync_unlock)(struct irq_data *data);
int (*irq_get_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool *state);
int(*irq_set_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool state);
unsigned long flags;
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
基础信息
- parent_device:指向这个中断控制器所属上级设备的指针。
- name:在
/proc/interrupts
文件中显示的名称,用于标识这个中断控制器。
中断控制函数
- irq_enable:启用中断的功能。如果未指定(NULL),默认会调用
irq_unmask
来启用。 - irq_disable:禁用中断,阻止中断触发。
- irq_ack:在新中断刚发生时立即调用。部分控制器不需要此功能。它的作用是标记中断已开始处理,可能通过禁用中断来避免后续干扰,直到当前中断处理完成。
- irq_mask:屏蔽硬件中断源,使其无法再触发中断。
- irq_unmask:取消屏蔽,重新允许中断触发。
中断处理收尾
- irq_eoi:表示“中断结束”(End Of Interrupt)。在中断服务完成后立即调用,用于重置控制器状态,以便后续中断能正常触发。某些情况下,它会执行与
irq_ack
相反的操作(比如启用中断)。
高级功能
- irq_set_affinity:在多核(SMP)系统中设置中断处理的 CPU。例如,指定某个 CPU 处理该中断。单核系统不需要此功能。
- irq_retrigger:强制重新触发已屏蔽的中断,让 CPU 再次收到该中断信号。
- irq_set_type:设置中断的触发类型(如电平触发或边沿触发)。
电源管理与状态
- irq_set_wake:启用或禁用中断的“唤醒功能”,让硬件能在休眠时通过中断唤醒系统。
总线访问控制
- irq_bus_lock:锁定慢速总线(如 I2C)的访问权限,确保操作期间无其他干扰。
- irq_bus_sync_unlock:同步并解锁之前锁定的总线,恢复总线的正常访问。
状态管理
- irq_get_irqchip_state:获取中断控制器的内部状态。
- irq_set_irqchip_state:设置中断控制器的内部状态。
这些函数共同管理中断控制器的行为,从触发到处理再到收尾,确保硬件中断能被操作系统正确响应和控制。
二、中断控制器域:irq_domain
在Linux系统中,中断控制器需要管理硬件中断号与系统内部编号之间的对应关系。这个功能通过一个叫做struct irq_domain
的结构体来实现。它的核心作用是:
- 基本功能: 将物理硬件的中断号(硬件IRQ)转换为系统内部统一管理的虚拟中断号(Linux IRQ),让操作系统能正确处理不同硬件的中断请求。
- 结构组成:
struct irq_domain {
// 区域名(比如"PCI中断域")
const char *name;
// 操作函数集合(负责具体转换逻辑)
const struct irq_domain_ops *ops;
// 硬件相关私有数据(比如芯片型号信息)
void *host_data;
// 控制行为的标志位(比如是否启用某些特性)
unsigned int flags;
// 可选字段:
// 固件节点(记录硬件设备在固件中的位置信息)
struct fwnode_handle *fwnode;
// 其他扩展字段...
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
关键解释:
- ops(操作集合):包含中断映射、分配等具体操作的函数,比如"如何把硬件号转成系统号"。
- host_data:保存该域对应的硬件设备特定信息(比如某个芯片的寄存器地址)。
- fwnode:记录设备在固件(如UEFI/Device Tree)中的位置,方便系统定位硬件。
简单来说,这个结构就像一个翻译器:当硬件发出中断信号时,irq_domain
根据硬件编号找到对应的系统内部编号,让操作系统知道"这个中断该由哪个驱动来处理"。
- API解释:
中断控制器驱动程序需要通过特定函数创建和注册irq_domain
(中断域),这个过程会根据硬件中断号(hwirq)和Linux系统中断号之间的映射方式选择不同的方法。以下是两种常用方法的说明:
①**irq_domain_add_linear()**
(线性映射)
原理:使用一个固定大小的表格,表格的索引是硬件中断号(hwirq)。当映射硬件中断时,系统会为每个硬件中断分配一个Linux的中断描述符(irq_desc),并将对应的Linux中断号存入表格中。
适用场景:
- 硬件中断数量较少且固定(比如256个以下)。
- 优点:查找速度快(直接通过表格索引),且只给实际使用的中断分配资源。
- 缺点:表格的大小需要和最大的hwirq号一样大,如果硬件中断号范围很大,会占用较多内存。
总结:这是大多数驱动程序的首选方法,适合中断数量少且明确的场景。
②**irq_domain_add_tree()**
(树形映射)
原理:用树结构来管理硬件中断号(hwirq)和Linux中断号的映射关系。当映射硬件中断时,系统同样会分配中断描述符,但通过树的层级结构查找对应的Linux中断号。
适用场景:
- 硬件中断号可能非常大(比如超过几千),不适合用线性表格。
- 优点:不需要预先分配大表格,节省内存。
- 缺点:查找速度取决于树中已存在的节点数量(节点越多,速度越慢)。
总结:适合硬件中断号范围极大或不确定的场景,但实际使用较少。
三、描述IRQ:irq_desc
3.1、irq_desc
Linux系统通过struct irq_desc
结构来管理每个中断(IRQ)。这个结构记录了中断的所有相关信息,比如处理函数、统计信息和硬件配置等。
关键字段解释
irq_common_data 存储与中断控制器硬件相关的通用数据,用于底层硬件操作。
kstat_irqs 记录每个CPU自系统启动以来收到的中断次数(统计信息)。
handle_irq 指向处理中断的主函数,负责执行中断的核心逻辑(比如调用设备驱动的处理函数)。
action 一个链表,存储所有注册到该中断的处理函数(
irqaction
结构)。 例如:多个设备共享同一个中断时,每个设备的处理函数都会被添加到这个链表中。irqs_unhandled 统计未被正确处理的中断次数(调试用)。
lock 一个多处理器系统的锁,确保中断处理在多核CPU下安全执行。
threads_active 记录当前有多少线程正在处理该中断(用于线程化中断)。
wait_for_threads 等待队列,用于同步等待中断处理线程完成。
nr_actions 统计该中断下注册的处理函数数量。
no_suspend_depth 和 force_resume_depth 跟踪中断是否设置了特殊标志(如“禁止休眠”或“强制唤醒”)。
dir 指向
/proc/irq/
目录下该中断的文件节点(用于调试或查看信息)。name 中断的名称(如“eth0”),在
/proc/interrupts
文件中显示。
3.2、中断操作结构(irqaction结构)
每个注册的中断处理函数对应一个struct irqaction
结构,它记录了该中断的详细信息:
关键字段解释
- handler 中断的直接处理函数(也称“硬中断处理程序”),负责快速处理中断的核心逻辑。
- thread_fn 线程化处理函数(可选),用于耗时操作(如数据拷贝),避免阻塞中断上下文。
- dev_id 设备的唯一标识(如设备对象的指针),用于区分共享中断的不同设备。
- name 设备名称(如“USB控制器”),在日志或
/proc/interrupts
中显示。 - next 链表指针,指向下一个共享该中断的
irqaction
结构。 - flags 中断标志(如
IRQF_SHARED
表示共享中断),控制中断行为。
3.3、中断芯片数据(irq_data结构)
struct irq_data
用于与硬件中断控制器交互,包含以下关键信息:
- irq Linux系统分配的中断号(如
16
)。 - hwirq 硬件设备的物理中断号(如芯片上的实际引脚编号)。
- chip 中断控制器的驱动接口,包含操作硬件的函数(如启用/禁用中断)。
- domain 负责将硬件中断号(
hwirq
)映射到Linux的中断号(irq
)。 - chip_data 硬件控制器的私有数据,用于存储特定配置。