简介

当调用request_irqrequest_threaded_irq注册中断处理函数时,内核就会构造一个irqaction结构体。在里面保存name、dev_id等,最重要的是handlerthread_fnthread

  1. struct irqaction {
  2. irq_handler_t handler;
  3. void *dev_id;
  4. void __percpu *percpu_dev_id;
  5. struct irqaction *next;
  6. irq_handler_t thread_fn;
  7. struct task_struct *thread;
  8. struct irqaction *secondary;
  9. unsigned int irq;
  10. unsigned int flags;
  11. unsigned long thread_flags;
  12. unsigned long thread_mask;
  13. const char *name;
  14. struct proc_dir_entry *dir;
  15. } ____cacheline_internodealigned_in_smp;

handler是中断处理的上半部函数,用来处理紧急的事情。
thread_fn对应一个内核线程thread,当handler执行完毕,Linux内核会唤醒对应的内核线程。在内核线程里,会调用thread_fn函数。
可以提供handler而不提供thread_fn,就退化为一般的request_irq函数。
也可以既提供handler也提供thread_fn,这就是中断上半部、下半部。
在reqeust_irq时可以传入dev_id,为何需要dev_id?作用有以下两个作用:

  • 中断处理函数执行时,可以使用dev_id
  • 卸载中断时要传入dev_id,这样才能在action链表中根据dev_id找到对应项

所以在共享中断中必须提供dev_id,非共享中断可以不提供。
可以参考

共享中断做起来比较浪费时间。简单的可以从单片机只有一个中断入口理解,产生中断后,在这个入口里面判断标志位,查看是什么中断产生的。不过这是很低端的单片机了。那么共享中断就是表示,多个外设的中断只有一个中断入口。当这个中断入口被调用的时候。遍历对应链表里面的中断函数。在这个函数里面来判断是否产生了中断。
换句话说,假设有A,B,C,D四个外设共享一个10号中断。那么当D中断产生了以后,10号中断的入口函数被调用。然后遍历A,B,C,D的中
断服务函数。每个中断函数都会被执行。但是A,B,C的中断函数一执行,判断不是此中断产生,就会返回。直到D的中断处理被调用。
共享中断如此麻烦,现在的硬件一般都不会采用共享中断的模式了。

request_irq 函数会注册一个irqaction结构。 然后最重要的操作是填充 handler。这个就是最常用的中断处理函数。

thread_fn这个参数就需要提到中断线程化了。这个参数就是新增的request_threaded_irq 函数 函数填充的变量。

  1. __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
  2. {
  3. if (new->thread_fn && !nested) {
  4. ret = setup_irq_thread(new, irq, false);
  5. ...
  6. }
  7. setup_irq_thread(struct irqaction *new, unsigned int irq, bool secondary)
  8. {
  9. struct task_struct *t;
  10. if (!secondary) {
  11. t = kthread_create(irq_thread, new, "irq/%d-%s", irq, new->name);
  12. ...
  13. }

中断线程化

参考Linux中的中断处理机制 [六] 文章。

中断处理方式

我们知道中断的上下文一般有以下四种形式。

  1. Workqueue
  2. Threaded IRQs
  3. Softirq
  4. Tasklets

    Workqueue

    参考

每一个内核维持了一个 worker_pool 。 属于绑定队列(bound queues)。一个worker_pool 里面可能包含了一个或者多个worker。这个worker可以简单的理解为
一个执行工作的线程。当满足要求以后,这个worker就会执行特定的work.
image.png
**

Threaded IRQs

参考文档

线程中断化使用的是 request_threaded_irq 函数。注册了一个irqaction结构体。填充了thread_fn以及handler
request_threaded_irq()的主要作用就是填写”irq_action”结构体,包括第二级的处理函数”handler”和”thread_fn”,区分共享IRQ的不同设备的”dev_id”和”devname”等。

Softirq


Tasklets

历史


参考资料

韦东山:剥丝抽茧分析Linux中断系统中的重要数据结构
Linux 设备驱动 Edition 3
源码解读Linux等待队列 - Gityuan博客 | 袁辉辉的技术博客
3-工作队列使用-Workqueue
Linux的中断处理机制 [三] - hardirq
Linux中的中断处理机制 [六]


**