本文已参与【新人创作礼】活动,一起开启掘金创作之路。
Linux中断系统变化并不大,比较重要新的是引入内核线程threaded_irq来处理中断。
硬件中断
Linux系统把中断的意义扩展了,对于按键中断等硬件产生的中断,称之为“硬件中断”(hard irq)。
不同的硬件又不同的中断号,对应的中断处理函数也不同。
软件中断
还可以人为地制造中断:软件中断(soft irq),如下图所示:
中断相关的代码
//include/linux/interrupt.h
enum
{
HI_SOFTIRQ=0,
TIMER_SOFTIRQ,
NET_TX_SOFTIRQ,
NET_RX_SOFTIRQ,
BLOCK_SOFTIRQ,
IRQ_POLL_SOFTIRQ,
TASKLET_SOFTIRQ,
SCHED_SOFTIRQ,
HRTIMER_SOFTIRQ, /* Unused, but kept as tools rely on the numbering. Sigh! */
RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */
NR_SOFTIRQS
};
/* 设置softirq_veq[nr]的标记位 */
extern void raise_softirq(unsigned int nr);
/* 设置软件中断的处理函数 */
extern void open_softirq(int nr, void (*action)(struct softirq_action *));
中断处理原则
不能嵌套
官方资料 中断处理函数触发时,需要把当前的函数压入栈中。如果中断能够嵌套,那么如果短时间内大量中断。 栈会迅速耗尽,为了防止这种情况的发生,也为了简单处理中断,Linux系统中断无法嵌套。 即当前中断没有被处理完前,不会处理另一个中断。
越快越好
因为中断的处理函数,CPU不能进行调度(无法嵌套)。所以在中断处理函数中,应该越简单越好。 保证尽快的被处理,进程调度靠定时器中断来实现。 在Linux中使用下面的函数来注册中断处理函数:
static inline int __must_check
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev);
中断处理过程
如果事情太多,拆分为上半部、下半部
如果中断需要做的事情过于复杂,那么我们应该把中断要做的事情进行切分:紧急的、不紧急的。
下半部实现的方式很多种,主要有tasklet和work queue
下半部简单:tasklet(小任务)
当下半部比较耗时但是能忍受,并且它的处理比较简单时,可以用tasklet来处理下半部。tasklet是使用软件中断来实现。
一些主要的中断处理流程:
下半部负责:workqueue(工作队列)
内核线程是系统是系统帮忙创建的,一般是kworker线程:
kworker线程要去"工作队列"(work queue)取出一格个"work",来执行它里面的函数。
//创建work函数:
static DECLARE_WORK(aer_recover_work, aer_recover_work_func);
//第一个参数是work结构体,第二个参数是要执行的函数
//要执行这个函数时,work提交给work queue
schedule_work(&aer_recover_work);
//函数会把work提供给系统默认的work queue:system_wq,它是一个队列
//当线程抢到时间来运行时,就会从work队列中获取函数并执行
//所以想要把需要的work提交到work queue
//需要在中断上半部中,调用schedule_work函数
总结:
- 很耗时的中断处理,应该放到线程里去
- 可以使用work、work queue
- 在中断上半部调用schedule_work函数,触发work的处理
- 既然是在线程中运行,那对应的函数可以休眠。
新技术(thread_irq)
/* irq:哪个中断
* handler:上半部函数,可以为空
* thread_fn:在线程里运行的函数
*/
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);
可以只提供thread_fn,系统会为这个函数创建一个内核线程。发生中断时,内核线程就会执行这个函数。 为每一个中断都创建一个内核线程,多个中断的内核线程可以分配到多个CPU上执行,提高了效率。