
一、request_irq 函数详细分析
request_threaded_irq 是 Linux 内核用来管理中断的核心函数,它的主要作用是把硬件中断和软件处理逻辑绑定起来。
- 申请中断权限:这个函数会向系统申请一个特定编号的硬件中断。
- 绑定处理函数:通过
handler参数,把你要写的中断处理函数和这个中断号绑定。 - 支持后台线程处理:可以设置第二个线程函数(
thread_fn)。 - 设置中断行为规则:用
irqflags参数告诉系统这个中断的触发方式。 - 关联设备信息:通过
dev_id参数可以把中断和具体设备绑定。 - 返回申请结果:申请成功返回 0,失败返回错误码。
int request_threaded_irq(unsigned int irq, irq_handler_t handler,
irq_handler_t thread_fn, unsigned long irqflags,
const char *devname, void *dev_id)
{
struct irqaction *action;
struct irq_desc *desc;
int retval;
if (irq == IRQ_NOTCONNECTED)
return -ENOTCONN;
if (((irqflags & IRQF_SHARED) && !dev_id) ||
(!(irqflags & IRQF_SHARED) && (irqflags & IRQF_COND_SUSPEND)) ||
((irqflags & IRQF_NO_SUSPEND) && (irqflags & IRQF_COND_SUSPEND)))
return -EINVAL;
desc = irq_to_desc(irq);
if (!desc)
return -EINVAL;
if (!irq_settings_can_request(desc) ||
WARN_ON(irq_settings_is_per_cpu_devid(desc)))
return -EINVAL;
if (!handler) {
if (!thread_fn)
return -EINVAL;
handler = irq_default_primary_handler;
}
action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
if (!action)
return -ENOMEM;
action->handler = handler;
action->thread_fn = thread_fn;
action->flags = irqflags;
action->name = devname;
action->dev_id = dev_id;
retval = irq_chip_pm_get(&desc->irq_data);
if (retval < 0) {
kfree(action);
return retval;
}
retval = __setup_irq(irq, desc, action);
if (retval) {
irq_chip_pm_put(&desc->irq_data);
kfree(action->secondary);
kfree(action);
}
return retval;
}2
3
4
5
6
7
8
9
10
11
12
13
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
1、调用 irq_to_desc 函数
irq_to_desc 函数是用来根据中断号找到对应的中断描述符结构的。它的查找方式分两种情况:

- 常规情况:当所有中断号都是连续排列时,系统会把这些
struct irq_desc结构体统一存放在一个数组里。这时候只需要用中断号作为索引直接查找。 - 特殊情况:如果系统配置了非连续的中断号,系统会把中断描述符存放在一个叫"基数树"的数据结构里。
简单总结
连续编号用数组直接找,不连续就用基数树快速查。

文件位置
kernel/irq/irqdesc.c


2、调用 __setup_irq 函数
文件位置
irq/manage.c
__setup_irq 函数用于设置中断处理程序。它的核心逻辑是:当某个中断描述符(irq_desc)已经有已注册的中断处理动作(irqaction)时,新添加的处理动作会被追加到动作链表的最后面。如果配置了需要以独立线程方式运行处理函数,就会通过 setup_irq_thread 创建对应的内核线程,并用 wake_up_process 唤醒这个线程开始工作。

中断线程化的原理:
文件位置
kernel/sched/core.c


二、中断的描述结构分析
irq_desc结构体:中断描述符irqaction结构体:中断动作
1、struct irq_desc
struct irq_desc {
struct irq_common_data irq_common_data;
struct irq_data irq_data;
unsigned int __percpu *kstat_irqs;
irq_flow_handler_t handle_irq; // 中断处理函数
struct irqaction *action; // IRQ action list
unsigned int status_use_accessors;
unsigned int core_internal_state__do_not_mess_with_it;
unsigned int depth; // nested irq disables
unsigned int wake_depth; // nested wake enables
unsigned int tot_count;
unsigned int irq_count;
unsigned long last_unhandled;
unsigned int irqs_unhandled;
atomic_t threads_handled;
int threads_handled_last;
raw_spinlock_t lock; // 自旋锁
struct cpumask *percpu_enabled;
const struct cpumask *percpu_affinity;
/* ... */
struct mutex request_mutex;
int parent_irq;
struct module *owner;
const char *name;
};2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
struct irq_desc 是 Linux 内核中用于描述中断的核心数据结构之一。每个硬件中断都对应一个 irq_desc 实例。
中断处理函数管理:handle_irq
irq_desc 中的 handle_irq 字段是一个函数指针,指向具体的中断处理函数。当中断发生时,内核会根据硬件触发的中断号找到对应的 irq_desc 实例,并调用 handle_irq 所指向的函数来执行中断处理逻辑。
中断行为管理:action
action 字段指向一个中断行为链表。每个节点代表一个注册到该中断上的回调函数及其相关联的数据。这种设计允许多个设备共享同一个中断号(即"中断共享"机制)。
中断统计信息:kstat_irqs
kstat_irqs 字段是一个每 CPU 的计数器数组,用于记录每个中断的触发次数。这些统计信息对于分析系统的中断负载分布、识别潜在的瓶颈以及优化性能至关重要。
中断状态管理
irq_desc 中的多个字段共同构成了中断状态管理系统:
- 嵌套中断禁用计数(
depth):用于跟踪当前中断是否被显式禁用。 - 唤醒使能计数(
wake_depth):用于管理中断在低功耗模式下的唤醒能力。 - 活动线程计数:用于跟踪当前正在运行的中断线程数量。
中断亲和性与负载均衡:affinity
现代多核处理器系统中,中断的 CPU 亲和性管理是优化性能的重要手段。irq_desc 中的 affinity 字段存储了一个 CPU 掩码,用于指示哪些 CPU 核心可以处理该中断。
2、irqaction 的核心作用
struct irqaction 是 Linux 内核中用于描述中断行为的数据结构之一。它的主要功能是将中断号(IRQ)与具体的中断处理逻辑绑定在一起。每个中断源都可以注册一个或多个 irqaction 实例,这些实例通过链表的形式连接起来,从而支持共享中断线的能力。
struct irqaction {
irq_handler_t handler; // 中断处理函数
void *dev_id;
void __percpu *percpu_dev_id;
struct irqaction *next;
irq_handler_t thread_fn;
struct task_struct *thread;
struct irqaction *secondary;
unsigned int irq; // 中断号
unsigned int flags; // 中断的标记
unsigned long thread_flags;
unsigned long thread_mask;
const char *name;
struct proc_dir_entry *dir;
};2
3
4
5
6
7
8
9
10
11
12
13
14
15
handler:中断处理的核心回调函数
handler 是 struct irqaction 中最重要的成员之一,它指向一个函数指针类型 irq_handler_t,即中断处理函数。当某个中断被触发时,内核会调用该函数来执行具体的中断处理逻辑。
注意
handler 函数通常运行在中断上下文中,这意味着它必须尽可能快地完成任务,避免长时间占用 CPU 资源。因此,复杂或耗时的操作通常会被推迟到下半部(如软中断或工作队列)中执行。
next:支持共享中断的链表结构
next 指针的存在揭示了 struct irqaction 的一个重要特性:它是一个链表节点。对于共享中断线的情况,多个 irqaction 实例会通过 next 指针串联在一起,形成一个链表。
dev_id 和 percpu_dev_id:设备标识与每 CPU 数据
dev_id 是一个通用的设备标识符,通常用于区分共享中断线上的不同设备。percpu_dev_id 则是一个每 CPU 数据指针,用于支持多核系统中的中断处理。
thread_fn 和 thread:线程化中断的支持
现代 Linux 内核引入了线程化中断(Threaded IRQ)的概念,允许将中断处理逻辑放在内核线程中执行。thread_fn 是线程化中断的回调函数,而 thread 则指向与该中断相关联的内核线程。
flags:中断行为的控制标志
flags 是一个无符号整数字段,用于定义中断处理的行为和特性。通过对 flags 的合理配置,驱动开发者能够精确控制中断的行为。
IRQF_SHARED:表示该中断可以与其他设备共享同一中断线IRQF_ONESHOT:指示内核在执行完线程化中断处理函数后手动重新启用中断IRQF_NO_THREAD:明确禁止将中断处理逻辑线程化
name:中断源的可读标识
name 是一个字符串指针,用于为中断源提供一个可读的名称。在 /proc/interrupts 文件中,每个中断号旁边都会显示其对应的 name 值。
dir:与 /proc 文件系统的集成
dir 是一个指向 struct proc_dir_entry 的指针,用于将中断信息暴露给 /proc 文件系统。
secondary:辅助中断的支持
secondary 是一个指向另一个 struct irqaction 实例的指针,用于支持辅助中断(Secondary IRQ)。
thread_flags 和 thread_mask:线程化中断的高级控制
thread_flags 包含一组标志位,用于指定线程的优先级、调度策略以及其他运行时特性;thread_mask 则是一个位掩码,用于限制线程在特定 CPU 上的运行。