目录
1 引入
2 内核机制
2.1 调用 request_threaded_irq 后内核的数据结构
2.2 request_threaded_irq
2.3 中断的执行过程
1 引入
复杂、耗时的事情,尽量使用内核线程来处理。工作队列用起来挺简单,但是它有一个缺点:工作队列中有多个 work,前一个 work 没处理完会影响后面的 work。解决方法有很多种,比如干脆自己创建一个内核线程,不跟别的 work 凑在一块了。在 Linux 系统中,对于存储设备比如 SD/TF 卡,它的驱动程序就是这样做的,它有自己的内核线程。
对于中断处理,还有另一种方法:threaded irq,线程化的中断处理。中断的处理仍然可以认为分为上半部、下半部。上半部用来处理紧急的事情,下半部用一个内核线程来处理,这个内核线程专用于这个中断。
内核提供了这个函数:
你可以只提供 thread_fn,系统会为这个函数创建一个内核线程。发生中断时,系统会立刻调用 handler 函数,然后唤醒某个内核线程,内核线程再来执行thread_fn 函数。
2 内核机制
2.1 调用 request_threaded_irq 后内核的数据结构
2.2 request_threaded_irq
request_threaded_irq 函数,肯定会创建一个内核线程。 源码在内核文件 kernel\irq\manage.c 中,
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)
{
// 分配、设置一个 irqaction 结构体
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 = __setup_irq(irq, desc, action); // 进一步处理
}
__setup_irq 函数代码如下(只摘取重要部分):
if (new->thread_fn && !nested) {
ret = setup_irq_thread(new, irq, false);
setup_irq_thread 函数代码如下(只摘取重要部分):
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;
}
new->thread = t;
2.3 中断的执行过程
对于 GPIO 中断, 调用关系如下,反过来看:
Breakpoint 1, gpio_keys_gpio_isr (irq=200, dev_id=0x863e6930) at drivers/input/keybo
ard/gpio_keys.c:393
393 {
(gdb) bt
#0 gpio_keys_gpio_isr (irq=200, dev_id=0x863e6930) at drivers/input/keyboard/gpio_k
eys.c:393
#1 0x80270528 in __handle_irq_event_percpu (desc=0x8616e300, flags=0x86517edc) at ke
rnel/irq/handle.c:145
#2 0x802705cc in handle_irq_event_percpu (desc=0x8616e300) at kernel/irq/handle.c:18
5
#3 0x80270640 in handle_irq_event (desc=0x8616e300) at kernel/irq/handle.c:202
#4 0x802738e8 in handle_level_irq (desc=0x8616e300) at kernel/irq/chip.c:518
#5 0x8026f7f8 in generic_handle_irq_desc (desc=<optimized out>) at ./include/linux/i
rqdesc.h:150
#6 generic_handle_irq (irq=<optimized out>) at kernel/irq/irqdesc.c:590
#7 0x805005e0 in mxc_gpio_irq_handler (port=0xc8, irq_stat=2252237104) at drivers/gp
io/gpio-mxc.c:274
#8 0x805006fc in mx3_gpio_irq_handler (desc=<optimized out>) at drivers/gpio/gpio-mx
c.c:291
#9 0x8026f7f8 in generic_handle_irq_desc (desc=<optimized out>) at ./include/linux/i
rqdesc.h:150
#10 generic_handle_irq (irq=<optimized out>) at kernel/irq/irqdesc.c:590
#11 0x8026fd0c in __handle_domain_irq (domain=0x86006000, hwirq=32, lookup=true, regs
=0x86517fb0) at kernel/irq/irqdesc.c:627
#12 0x80201484 in handle_domain_irq (regs=<optimized out>, hwirq=<optimized out>, dom
ain=<optimized out>) at ./include/linux/irqdesc.h:168
#13 gic_handle_irq (regs=0xc8) at drivers/irqchip/irq-gic.c:364
#14 0x8020b704 in __irq_usr () at arch/arm/kernel/entry-armv.S:464
我 们 只 需 要 分 析 __handle_irq_event_percpu 函 数 , 它 在kernel\irq\handle.c 中:
线程的处理函数为 irq_thread,代码在 kernel\irq\handle.c 中: