《linux内核设计与实现》
中断和中断处理
1、中断
中断是硬件发出通知给处理器。
键盘输入->处理器接收到中断->向操作系统反映此信号到来->操作系统做出处理。
对于不同的设备对应的中断不同,每个中断都会有一个唯一的数字标志,这使得系统才能给不同中断提供对应的中断处理程序。
中断值通常被称为中断请求(IRQ)线,每个IRQ线都会被关联一个数值量,如PC机上,IRQ 0是时钟中断,IRQ 1是键盘中断,但有些并不是这么严格定义的,如在PCI总线上的设备而言,中断是动态分配的,无论如何,都需要让内核知道对应的信息。
2、中断处理程序
在处理特定中断时,内核需要执行一个中断服务程序(ISR)。中断处理程序与其他内核函数区别于前者是被内核用来响应中断,运行于中断上下文的特殊的上下文中,中断上下文也可称为原子上下文,该上下文的代码不可阻塞。
3、上半部与下半部的对比
想要中断运行快,又想中断处理程序工作量多,因此将中断分为上下半部分,上半部分主要是对接收的中断进行应答,这一部分是对其他所有中断禁止情况下实现的,能够允许稍后执行的推到下半部分执行,此后内核会在合适的时机执行。
如网卡:当网卡接收到数据包之后,需要通过中端通知内核,由于数据包一般都很大,而内存缓冲区是固定的,如果不及时处理可能就会导致内存溢出,因此内核上半部就需要及时缓存数据,而对于数据包的解析和处理就放于下半部。
4、注册中断处理程序
中断处理程序是管理硬件的驱动程序的组成部分,如果需要使用中断,则需要注册一个中断处理程序。
/***********************************************************
* irq表示要分配的中断号,1、通过探测获取,2、通过动态确定
* handler指向实际的中断处理程序在第5节
* 其他参数在4.1小节
***********************************************************/
int request_irq(unsigned int irq,irq_handler_t handler, unsigned long flags, const char *name, void *dev);
4.1 中断处理程序标志
1.flags
在request_irq函数中有一个flags参数,其决定了触发方式,其是一个或者多个标志的位掩码,定义在<linux/interrupt.h>中
IRQF_DISABLED:标志位被设置后,意味着内核在处理中断处理程序期间要禁用其他中断,如果没设置则表示出本身之外其他任何中断可以同时执行。
IRQF_SAMPLE_RANDOM:标志这个设备产生的中断对内核的熵池有贡献。内核熵池负责提供从各种随机事件导出的真正的随机数,那么来自该设备的中断间隔时间就会最为熵填充到熵池。若我们的设备是以一种预知的速率产生中断(如定时器中断)或者受到外部供给,那么就不需要设置,反之大部分的设备中断速率都是不可知的,则可能成为一个较好的熵源。
IRQF_TIMER:系统定时器中断处理。
IRQF_SHARED:表示可以在多个中断处理程序之间共享中断线,如果给定的irq相同则必须设置该位。
IRQF_TRIGGER_FALLING 下降沿
IRQF_TRIGGER_RISING 上升沿
IRQF_TRIGGER_LOW 低电平
IRQF_TRIGGER_HIGH 高电平
2.name
与中断相关的设备的ASCII文本表示,如键盘中断对应的值为"keyboard"。
3.dev
用于共享中断线。如果中断线是被共享的,那么就需要设置,反之设置为NULL。
内核每次调用中断处理程序handler时会把该参数传给他,在实际中,常常是将设备结构体传入进去。
4、request_irq
成功返回0,失败返回非0
如果有错误发生,指定的中断程序不会被注册,常见的是-EBUSY,表明该中断线正在被使用。
4.2 一个中断例子
注意在注册中断处理函数之前就必须先对硬件进行初始化。
request_irq():
if (request_irq(irqn, my_interrupt, IRQF_SHARED, "my_device", my_dev))