以下的语句都是摘自网络上的资料,自己再综合总结一下。
Linux的中断处理遵循了“重要的事情,马上做。不重要的事情,推后做”。
MIPS平台下的do_IRQ处理函数。
1: void __irq_entry do_IRQ(unsigned int irq)
2: {
3: irq_enter();
4: __DO_IRQ_SMTC_HOOK(irq);
5: generic_handle_irq(irq);
6: irq_exit();
7: }
MIPS平台下的generic_handle_irq经过层层调用,会调用对应模块通过request_irq函数注册的中断处理函数。
一般情况下,中断处理函数是在关中断的情况下运行的,为了提高效率,尽量减少关中断的时间,于是有了soft_irq机制,把一部分不需要在关中断情况下执行的代码放在中断外面执行。具体就是调用raise_softirq去设置一个软中断,由open_softirq来注册对应的软中断处理程序,用来处理一些不太紧急的中断处理程序。
在软中断机制中,为每个CPU维护了一个若干位的掩码集,每位掩码代码一个中断号,每位中断号于raise_softirq绑定的中断号相对应。
中断有其对应的优先级,每个CPU处理属于自己的中断。在软中断处理函数中开中断进行处理。
实际上,软中断很少直接使用,由tasklet机制来调用软中断。实际中,在进入中断处理程序后,在完成关中断的部分,调用tasklet_schedule/tasklet_hi_schedule标记一个tasklet,中断处理程序结束。后面的工作由HI_SOFTIRQ/TASKLET_SOFTIRQ对应的软中断处理程序去处理被标记的tasklet。
softirq<---->每个CPU,每个CPU处理自己的softirq(可重入函数,可能在多个CPU上同时运行),常用于时钟中断处理过程和网络收发处理过程。
tasklet是在多个CPU间被串行化执行,处理函数不必考虑可重入的问题。
CPU中断处理完成后,需要恢复之前保存在栈上的寄存器信息,恢复中断之前的运行状态。
softirq | tasklet | workqueue |
轮询所有中断(限制软中断为32个) | 无差别队列机制,有中断时才执行
效率比softirq高,支持SMP,无数量限制 |
一组内核线程,作为中断守护线程来使用 |
尽量不用 | 推荐使用 | 要用到线程才能用到的某些机制时,推荐使用。 |
softirq —> tasklet —>workqueue
轮训处理所有软中断
可能存在的两方面的问题:
- 连续的低优先的中断可能持续占有CPU,而高优先的某些进程则无法获得CPU。
- 中断处理的整个阶段,不能调用可能导致睡眠的函数(信号量,分配内存,阻塞式IO申请等等)
对与第一个问题:
- 新版的Linux内核增加的ksoftirqd的内核线程。如果持续处理的softirq超过了一定数量,则结束中断处理程序,然后唤醒ksoftirq来继续处理。
- linux内核提供了工作队列机制来解决上述问题,workqueue。定义一个workqueue结构(包含了处理函数),在中断处理的过程中,调用schedule_work函数,work便加入到workqueue中,等待处理。工作队列有自己的处理线程,这些work被推迟到这些线程中处理,处理过程只可能发生在这些工作线程中,因此,加入到工作队列中的处理函数可以睡眠。