参考:2.6.34
看了下2.6.34中的中断线程,但是在《内核设计与实现》3ed_CN的p_90~p_130内并未提起中断线程,因此做下记录,其中关于kthread_create函数已在“工作队列”笔记中做了说明。
通常通过request_irq申请中断资源时并未注册中断线程处理函数,可以通过request_threaded_irq来注册中断线程处理函数。
注册中断线程处理函数:
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; | des = irq_to_desc(irq); | action = kzalloc(sizeof(struct irqaction), GFP_KERNEL); | |----action->handler = handler;中断处理函数 | action->thread_fn = thread_fn; 中断线程处理函数 | action->name = devname; | action->dev_id = dev_id; | |---->int retval = __setup_irq(irq, desc, action);
__setup_irq()中新建中断线程
static int __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) |---->int nested = desc->status & IRQ_NESTED_THREAD; | |此处考虑创建中断线程; |关于IRQ_NESTED_THREAD的意义,我并没有理解,存疑 |if (new->thread_fn && !nested) { | |----struct task_struct *t; | t = kthread_create(irq_thread, new, "irq/%d-%s", irq, new->name); | get_task_struct(t); | new->thread = t; |} | |----int shared = 0; | struct irqaction **old_ptr; | 通过遍历irq_desc中域irqaction*,获得可添加irqaction的位置; | 若为首次添加,则shared = 0;否则shared = 1; | 是否首次添加的区被至于在于对irq_desc实例的status域的影响, | 因为第一个irqaction将决定是否兼容后续的irqaction添加。 | 在__setup_irq中有一系列的兼容性检查,此分析中省略了。 |----若首次添加,则执行...... |----new->irq = irq; |----*old_ptr = new; | |----if(new->thread) wake_up_process(new->thread); 见本记录最后对此的疑问
若首次在irq_desc中域irqaction*添加irqaction,则执行如下流程:
|---->irq_chip_set_defaults(desc->chip); |---->init_waitqueue_head(&desc->wait_for_threads); |---->if(new->flags & IRQF_TRIGGER_MASK)设置中断被触发的类型。 |---->desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING | IRQ_OENSHOT | | IRQ_INPROGRESS | IRQ_SPURIOUS_DISABLED); |---->new->flags & IRQF_ONESHOT ? desc->status |= IRQ_ONESHOT :(void)0; |---->if(!desc->status & IRQ_NOAUTOEN){ | desc->depth = 0; | desc->status &= ~IRQ_DISABLED; | desc->chip->startup(irq); | }else desc->depth = 1;
何时唤醒中断线程?
handle_IRQ_event中,若中断处理函数返回值为IRQ_WEAK_THREAD,则wake_up_process(action->thread)
中断处理线程的执行:
static int irq_thread(void *data) { struct sched_param param = { .sched_priority = MAX_USER_RT_PRIO/2, }; struct irqaction *action = data; struct irq_desc *desc = irq_to_desc(action->irq); int wake, oneshot = desc->status & IRQ_ONESHOT; sched_setscheduler(current, SCHED_FIFO, ¶m); current->irqaction = action; while (!irq_wait_for_interrupt(action)) { +-- 18 lines: irq_thread_check_affinity(desc, action);-------------------------------------------------------------- { raw_spin_unlock_irq(&desc->lock); action->thread_fn(action->irq, action->dev_id); +-- 3 lines: if (oneshot)------------------------------------------------------------------------------------------ } +-- 3 lines: wake = atomic_dec_and_test(&desc->threads_active);---------------------------------------------------- if (wake && waitqueue_active(&desc->wait_for_threads)) wake_up(&desc->wait_for_threads); } +-- 5 lines: Clear irqaction. Otherwise exit_irq_thread() would make----------------------------------------------- current->irqaction = NULL; return 0; }
疑问:1、固然在__setup_irq的最后唤醒中断线程没有错误,但是为什么要在__setup_irq的最后唤醒中断线程,如果不唤醒会导致错误么?
2、内核中既有中断线程,又有工作线程,为何同时应入这两种方法?两者的区别和各自的优势又是什么?
3、当然,我们也注意到中断线程的调度策略总被设置为“SCHED_FIFO",而工作线程默认的调度策略是”SCHED_NORMAL", 为什么要把一个内核线程的调度策略设置成“SCHED_FIFO", SCHED_FIFO将一直持有cpu资源,要是在中断线程中做大量的工作,那系统的吞吐量将会大大降低?