
一、中断线程化使用
以前处理中断时,会用一个工作线程来排队处理所有中断任务。但这个工作线程只能在一颗 CPU 上运行,不管有多少中断请求,都只能由它一个线程处理。在多核电脑中明明有很多 CPU 闲置,却让所有中断任务都挤在同一颗 CPU 上处理,这显然不合理。
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);2
3
4
中断处理分为两个部分:
1、上半部(@handler 函数)
直接响应硬件中断的函数,会在中断发生时立即运行。它的任务是:
- 快速完成紧急处理(比如记录关键数据)
- 如果能在 0.1 毫秒内完成,直接处理完毕并返回
IRQ_HANDLED - 如果需要更长时间(超过 0.1 毫秒),则返回
IRQ_WAKE_THREAD触发下半部
2、下半部(@thread_fn 函数)
当上半部返回 IRQ_WAKE_THREAD 时,系统会启动一个内核线程来执行这个函数。它负责:
- 完成耗时的处理工作(比如数据传输或复杂计算)
- 必须在处理完成后返回
IRQ_HANDLED - 只有在上半部再次触发时才会重新运行
关键规则
- 上半部必须快速响应(不超过 0.1 毫秒)
- 耗时操作必须交给下半部处理
- 下半部运行期间不会被重复触发,需等待下次中断发生
新技术
threaded irq 为每一个中断都创建一个内核线程;多个中断的内核线程可以分配到多个 CPU 上执行,这提高了效率。
二、中断线程化使用案例
源代码下载
git clone git@gitee.com:yangxuesong314/linux-driver.git- 若之前已 git 拉取代码可以忽略
- 代码位于:
linux-driver/04.中断及异常/07.中断线程化实验
实验说明
- GPIO1_A4 →
1 * 32 + 0 * 8 + 4 = 36
驱动代码:
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
int irq;
// 中断处理函数的底半部(线程化中断处理函数)
irqreturn_t test_work(int irq, void *args)
{
msleep(1000);
printk("This is test_work\n");
return IRQ_RETVAL(IRQ_HANDLED);
}
// 中断处理函数的顶半部
irqreturn_t test_interrupt(int irq, void *args)
{
printk("This is test_interrupt\n");
return IRQ_WAKE_THREAD; // 唤醒线程
}
static int interrupt_irq_init(void)
{
int ret;
irq = gpio_to_irq(36);
printk("irq is %d\n", irq);
ret = request_threaded_irq(irq, test_interrupt, test_work,
IRQF_TRIGGER_RISING, "test", NULL);
if (ret < 0) {
printk("request_irq is error\n");
return -1;
}
return 0;
}
static void interrupt_irq_exit(void)
{
free_irq(irq, NULL);
printk("bye bye\n");
}
module_init(interrupt_irq_init);
module_exit(interrupt_irq_exit);
MODULE_LICENSE("GPL");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
驱动加载:

拉高 GPIO:

三、中断线程化原理
注意
有些中断不能用多线程处理,比如时钟中断。因为有些程序会一直霸占 CPU 不松手,这时操作系统只能通过定期触发的时钟中断强行夺回控制权。这类中断对系统调度至关重要,注册处理函数时必须明确标记为 IRQF_NO_THREAD。
1、irqaction 中包含 thread_fn
每个中断处理描述符(irqaction)对应一个内核线程。这个线程的信息由 irqaction 的 thread 属性保存,而 thread_fn 属性则指向该线程要执行的具体处理函数。

2、request_threaded_irq 原理
中断处理线程是实时内核线程,优先级设为 50,使用先进先出(SCHED_FIFO)调度策略。这类线程名称以 irq/ 开头,后面跟着 Linux 系统分配的中断编号,负责处理中断的函数是 irq_thread()。
调用链路:request_threaded_irq() → __setup_irq() → setup_irq_thread()
// kernel/irq/manage.c
static int
setup_irq_thread(struct irqaction *new, unsigned int irq, bool secondary)
{
struct task_struct *t;
struct sched_param param = {
.sched_priority = MAX_USER_RT_PRIO / 2,
};
if (!secondary) {
t = kthread_create(irq_thread, new, "irq/%d-%s", irq, new->name);
} else {
t = kthread_create(irq_thread, new, "irq/%d-s-%s", irq, new->name);
param.sched_priority -= 1;
}
if (IS_ERR(t))
return PTR_ERR(t);
sched_setscheduler_nocheck(t, SCHED_FIFO, ¶m);
get_task_struct(t);
new->thread = t;
set_bit(IRQTF_AFFINITY, &new->thread_flags);
return 0;
}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
调用栈:

irqreturn_t __handle_irq_event_percpu(struct irq_desc *desc, unsigned int *flags)
{
irqreturn_t retval = IRQ_NONE;
unsigned int irq = desc->irq_data.irq;
struct irqaction *action;
record_irq_time(desc);
for_each_action_of_desc(desc, action) {
irqreturn_t res;
trace_irq_handler_entry(irq, action);
res = action->handler(irq, action->dev_id);
trace_irq_handler_exit(irq, action, res);
switch (res) {
case IRQ_WAKE_THREAD:
if (unlikely(!action->thread_fn)) {
warn_no_thread(irq, action);
break;
}
__irq_wake_thread(desc, action);
case IRQ_HANDLED:
*flags |= action->flags;
break;
default:
break;
}
retval |= res;
}
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
中断处理线程的处理函数是 irq_thread(),调用函数 irq_thread_fn(),然后函数 irq_thread_fn() 调用注册的线程处理函数:
static int irq_thread(void *data)
{
struct callback_head on_exit_work;
struct irqaction *action = data;
struct irq_desc *desc = irq_to_desc(action->irq);
irqreturn_t (*handler_fn)(struct irq_desc *desc, struct irqaction *action);
if (force_irqthreads && test_bit(IRQTF_FORCED_THREAD, &action->thread_flags))
handler_fn = irq_forced_thread_fn;
else
handler_fn = irq_thread_fn;
init_task_work(&on_exit_work, irq_thread_dtor);
task_work_add(current, &on_exit_work, false);
irq_thread_check_affinity(desc, action);
while (!irq_wait_for_interrupt(action)) {
irqreturn_t action_ret;
irq_thread_check_affinity(desc, action);
action_ret = handler_fn(desc, action);
if (action_ret == IRQ_WAKE_THREAD)
irq_wake_secondary(desc, action);
wake_threads_waitq(desc);
}
task_work_cancel(current, irq_thread_dtor);
return 0;
}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
static irqreturn_t irq_thread_fn(struct irq_desc *desc, struct irqaction *action)
{
irqreturn_t ret;
ret = action->thread_fn(action->irq, action->dev_id);
irq_finalize_oneshot(desc, action);
return ret;
}2
3
4
5
6
7
8