Linux中断简介
和单片机中断类似,Linux中断是指CPU在运行程序过程中,由于内部或者外部的事件引起的使得CPU暂停正在运行的程序,去执行该内部事件或外部事件的引起的服务中去,服务执行完毕后再返回断点处继续执行的情形。
硬件中断和软件中断
Linux操作系统中对于中断的应用十分频繁。Linux将的中断可以分为硬件中断和软件中断。比如按键中断、网卡中断这些由硬件产生的中断称之为硬件中断(hard irq)。相对应地,这些硬件中断都有属于自己的中断处理函数。
void irq_function_key();
如上代码所示,当产生了按键中断时,对应的按键中断处理函数被调用。这是硬件中断的实现,相对的,我们还可以人为地实现软件中断。软件中断中,每个中断函数对应有一个flag位,如果我们通过代码将对应的flag位置1,则说明发生了该中断。我们可以通过进入include/linux/interrupt.h 来查看内核关于软件中断的源码
上面说到了软件中断的触发,我们刚才说了是设置对应的标志位,那么如何去设置标志位呢?最核心的函数是raise_softirq,目的就是去设置softirq_veq[nr]的标志位
extern void raise_softirq(unsigned nr);
触发了中断,自然需要有一个对应的中断处理函数。怎样设置中断处理函数呢,同样给出一个函数
extern void open_softirq(int nr, void( *action)(struct softirg_action *));
关于软件中断,在文章后续会继续介绍
Linux中断原则:不能嵌套
中断嵌套是指一个中断在执行的过程中,跳出当前中断,去执行下一个中断。Linux的中断是不能实现嵌套的,这是它和单片机的相异之处。原因如下:根据上文的描述,在触发中断时,系统就会执行相应的中断处理函数,系统会为其分配相应的栈内存,此时如果执行嵌套中断,那么系统会保存当前的状态(中断的保存机制),并进入新的中断处理函数,重新为其分配栈内存。如此往复,栈空间就会耗尽。所以为了避免此类情况的发生,Linux禁止中断嵌套的发生,即使在执行中断的A程序遇到了比他优先级更高的B程序的中断请求。
Linux中断的拆分
假设遇到以下一个场景:在执行A中断时,其他中断无法执行(系统处于关中断),B也发出中断请求。而当A中断执行时间过长时,该怎么办?
答:我们可以将其拆分成两类,重要的、不重要的。这个时候,我们引入一些新概念:上半部、下半部、tasklet、工作队列。具体请看后面的描述。
tasklet代码如下:
代码流程图如下:
假设硬件中断A的上半部函数为irq_top_half_A,下半部为irq_bottom_half_A。此时使用情景化的分析,方便去理解整段代码。
a. 硬件中断A处理过程中,没有其他中断发生:
一开始,preempt_count = 0;
上述流程图①~⑨依次执行,上半部、下半部的代码各执行一次。
b. 硬件中断A处理过程中,又再次发生了中断A:
一开始,preempt_count = 0;
执行到第⑥时,一开中断后,中断A又再次使得CPU跳到中断向量表。
注意:
这时preempt_count等于1,并且中断下半部的代码并未执行。
CPU又从①开始再次执行中断A的上半部代码:
在第①步preempt_count等于2;
在第③步preempt_count等于1;
在第④步发现preempt_count等于1,所以直接结束当前第2次中断的处理;
注意:
重点来了,第2次中断发生后,打断了第一次中断的第⑦步处理。当第2次中断处理完毕,CPU会继续去执行第⑦步。
可以看到,发生2次硬件中断A时,它的上半部代码执行了2次,但是下半部代码只执行了一次。
所以,同一个中断的上半部、下半部,在执行时是多对一的关系。
c. 硬件中断A处理过程中,又再次发生了中断B:
一开始,preempt_count = 0;
执行到第⑥时,一开中断后,中断B又再次使得CPU跳到中断向量表。
注意:
这时preempt_count等于1,并且中断A下半部的代码并未执行。
CPU又从①开始再次执行中断B的上半部代码:
在第①步preempt_count等于2;
在第③步preempt_count等于1;
在第④步发现preempt_count等于1,所以直接结束当前第2次中断的处理;
注意:
重点来了,第2次中断发生后,打断了第一次中断A的第⑦步处理。当第2次中断B处理完毕,CPU会继续去执行第⑦步。
在第⑦步里,它会去执行中断A的下半部,也会去执行中断B的下半部。
所以,多个中断的下半部,是汇集在一起处理的。
总结:
a. 中断的处理可以分为上半部,下半部
b. 中断上半部,用来处理紧急的事,它是在关中断的状态下执行的
c. 中断下半部,用来处理耗时的、不那么紧急的事,它是在开中断的状态下执行的
d. 中断下半部执行时,有可能会被多次打断,有可能会再次发生同一个中断
e. 中断上半部执行完后,触发中断下半部的处理
f. 中断上半部、下半部的执行过程中,不能休眠。假如中断休眠的话,就无法继续调度进程了