1.2.2 硬件中断
外部设备用硬件中断来通知操作系统有重要的事件发生,中断发生后,CPU会暂时停止当前程序的执行,转去执行中断处理程序,中断处理程序结束后再恢复原来被停止程序的执行。
硬件中断是一种系统资源,当我们为设备编写中断处理程序来处理外部事件时,要向系统申请中断资源(即硬件中断信号线,通常称为中断号),并将中断处理程序与中断源相关联。我们可以在运行时用内核提供的一对函数来申请和释放中断资源。
int request_irq(unsigned int irq,irq_handler_t handler,unsigned long flags, const char *devname, void *dev_id)
函数request-irq申请中断号为irq的资源,将申请到的中断号irq与设备名为devname,设备索引号为dev_id设备的中断处理程序handler相连。该设备的中断类型为flags。
void free_irq(unsigned int irq, void *dev_id)
函数free-irq释放中断资源。
在这里需要对中断类型标志flags做进一步说明,在网络子系统中,我们主要区分两类中断类型(虽然在内核中还有其他的中断类型)。
1.快速中断
快速中断(fast interrup)的中断处理程序运行时间非常短,所以当前被打断的活动暂停时间也很短。快速中断的特点是,屏蔽当前运行中断处理程序CPU的其他中断,这样中断处理程序的执行就不会被别的中断打断。要将中断申请注册为快速中断,需在申请中断资源时将中断类型标志flags设为SA_INTERRUPT。
2.慢速中断
慢速中断(slow interrupt)的中断处理程序在执行期间可以被别的中断打断。慢速中断处理程序执行的时间较长(相对快速中断而言),所以占用CPU的时间也长。
中断处理程序的执行通常可以暂停所有别的活动。不同的中断在几个CPU上可以同时运行,但某个中断的处理程序一次只能在一个CPU上执行。
如果你想查看当前CPU是否在中断活动中,则可以调用内核的API。
in_irq() include/asm/hardirq.h
3.top half和bottom half
硬件中断可以打断当前CPU上所有其他的活动,并会屏蔽CPU其他硬件中断。硬件中断是系统中有限的资源之一,硬件中断处理程序应尽可能快地执行完。在执行中断处理程序期间,如果设备有其他的外部事件发生,由于当前中断资源和CPU已被占用,其他的外部事件就不能得到CPU的响应,所以中断处理程序的执行要尽可能的快。但不是所有事件处理都可以在很短时间内完成,例如网络数据包的接收处理,从网络适配器收到的数据包到将数据包传送到用户接收进程,需要几千个CPU时钟才能完成。接收网络数据包这类事件虽然是由中断触发,但对网络数据包的处理却不能全部放在中断处理程序中来做。
为了节省系统资源,使中断处理程序的运行时间尽可能短,像上述这类费时的事件处理在Linux内核中将其分成两个部分来完成。
(1)top half
top half只完成中断触发后最重要的任务处理,这里top half就对应中断处理程序。在第5章我们分析网络设备驱动程序的中断处理程序时会看到,网络适配器的中断处理程序只将收到的数据包复制到内核的缓冲区后就立即结束返回,释放占有的中断和CPU。对数据包的协议分析和处理不在中断处理程序中进行。
(2)bottom half
bottom half完成所有非紧急部分的处理。bottom half代码由top half调度,放在以后某个安全的时间运行。比如上述的网络数据包复制到内核以后的分析处理,就在这部分代码中完成。
bottom half与top half最大的区别是,在bottom half执行期间,打开所有硬件中断。它们之间的关系是:top half将在设备缓冲区的数据复制到内核地址空间缓冲区,调度bottom half后退出,这个过程非常快。bottom half执行余下的处理任务,这样在bottom half工作过程中,CPU可以响应新的外部中断请求。Linux中有两种不同的机制来实现bottom half过程:tasklet和workqueue。