Linux request_threaded_irq线程化中断与handler_fnrequest_threaded_irq是Linux内核中注册中断处理函数的主要接口它将中断处理拆分为两个阶段上半部handler在硬中断上下文执行下半部thread_fn在专用内核线程中运行。这种做法显著降低了硬中断关闭时间避免了高优先级中断长时间阻塞系统。函数原型定义在kernel/irq/manage.ccint request_threaded_irq(unsigned int irq, irq_handler_t handler,irq_handler_t thread_fn, unsigned long flags,const char *name, void *dev)参数中handler是硬中断回调函数运行在中断关闭的原子上下文中thread_fn是线程化回调运行在可睡眠的进程上下文。当handler返回IRQ_WAKE_THREAD时内核唤醒该中断对应的irq线程执行thread_fn。函数内部的核心实现cint request_threaded_irq(unsigned int irq, irq_handler_t handler,irq_handler_t thread_fn, unsigned long flags,const char *name, void *dev){struct irqaction *action;struct irq_desc *desc;int retval;if (irq NR_IRQS)return -EINVAL;if (!handler !thread_fn)return -EINVAL;desc irq_to_desc(irq);if (!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 flags;action-name name;action-dev_id dev;retval __setup_irq(irq, desc, action);if (retval)kfree(action);return retval;}当handler为NULL而thread_fn不为空时内核自动填入irq_default_primary_handler。该函数是快速处理函数cstatic irqreturn_t irq_default_primary_handler(int irq, void *dev_id){return IRQ_WAKE_THREAD;}这意味着如果用户只提供thread_fn中断到达后立即唤醒线程不执行任何额外工作。这种做法适用于设备中断处理几乎不涉及时间敏感操作、所有逻辑可在进程上下文完成的场景。__setup_irq是将irqaction插入中断描述符action链表的关键函数cstatic int __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new){struct irqaction *old, **old_ptr;unsigned long flags;int ret, nested, shared 0;raw_spin_lock_irqsave(desc-lock, flags);old_ptr desc-action;old *old_ptr;if (old) {if (!(old-flags new-flags IRQF_SHARED))return -EBUSY;shared 1;}if (new-flags IRQF_THREAD) {ret setup_irq_thread(new, irq, desc);if (ret)return ret;}if (!shared) {ret irq_startup(desc, IRQ_RESEND, IRQ_START_FORCE);if (ret) {desc-istate | IRQS_NOAUTOEN;return ret;}}*old_ptr new;new-next old;raw_spin_unlock_irqrestore(desc-lock, flags);if (new-flags IRQF_SHARED)register_irq_proc(irq, desc);return 0;}irq线程在内核中是per-interrupt的由setup_irq_thread创建cstatic int setup_irq_thread(struct irqaction *new, unsigned int irq,struct irq_desc *desc){struct task_struct *t;t kthread_create(irq_thread, new, irq/%d-%s, irq, new-name);if (IS_ERR(t))return PTR_ERR(t);new-thread t;set_bit(IRQTF_RUNTHREAD, new-thread_flags);return 0;}线程名为irq/%d-%s格式其中%d是中断号%s是设备名。该线程在中断触发且handler返回IRQ_WAKE_THREAD时被唤醒执行new-thread_fn。线程主体irq_thread的循环逻辑cstatic int irq_thread(void *data){struct irqaction *action data;struct irq_desc *desc irq_to_desc(action-irq);irqreturn_t (*thread_fn)(int irq, void *dev_id) action-thread_fn;int irq action-irq;while (!irq_wait_for_interrupt(action)) {irqreturn_t action_ret;raw_spin_lock_irq(desc-lock);if (desc-istate IRQS_PENDING) {desc-istate ~IRQS_PENDING;raw_spin_unlock_irq(desc-lock);handle_irq_event(desc);} else {raw_spin_unlock_irq(desc-lock);}action_ret thread_fn(irq, action-dev_id);if (action_ret IRQ_HANDLED)local_inc(desc-threads_handled);}return 0;}IRQF_ONESHOT标志对线程化中断有特殊意义。当设置了IRQF_ONESHOT时内核在handler返回后保持中断屏蔽直到thread_fn执行完毕才调用chip-irq_unmask。这防止了中断源在thread_fn执行期间再次触发导致丢失中断或数据竞争。对于需要电平触发的设备IRQF_ONESHOT是标准配置cif (new-flags IRQF_ONESHOT)desc-istate | IRQS_ONESHOT;request_threaded_irq与request_irq的关系request_irq是request_threaded_irq的简化封装cstatic inline int __must_checkrequest_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev){return request_threaded_irq(irq, handler, NULL, flags, name, dev);}当thread_fn为NULL时内核不创建irq线程所有工作都在handler中完成。这也意味着request_irq注册的是纯硬中断处理函数不在进程上下文中运行。中断线程化的优点在于降低了关中断时间。对于频繁触发的中断handler仅记录必要硬件状态并返回IRQ_WAKE_THREAD耗时的数据处理由低优先级的irq线程完成。同时irq线程可以被调度器管理运行时可被其他更高优先级任务抢占避免了硬中断上下文不能被调度的限制。