Interrupts
硬件发送信号给进程产生中断,中断是异步产生的。中断由中断请求线(IRQ)来区分,IRQ是一个数值,一般来说0代表计时器,1代表键盘。
而异常一般是同步发生的,所以也叫做同步中断,当处理器执行指令时会产生异常:除0,页错误等等,当然还有前面讲过的系统调用。
Interrupt Handlers
中断处理函数也叫做中断服务程序,每个设备产生的中断都有一个相关的中断处理函数,中断处理函数是设备驱动的一部分。
中断处理函数运行的中断上下文中,在中断上下文中不会发生阻塞,不会发生进程调度,只可能被更高优先级的中断给中断。
Top Halves Versus Bottom Halves
中断分为两个部分,一个是top halves一个是bottom halves
中断服务程序就是top halves,关于buttom halvs以及为什么要分成两个部分下一章再讲。
Registering and Interrupt Handler
设备要使用中断必须先注册中断服务程序
/* request_irq: allocate a given interrupt line */
int request_irq(unsigned int irq,
irq_handler_t handler,
unsigned long flags,
const char *name,
voi *dev)
typedef irqreturn_t (*irq_handler_t)(int, void *);
其实就是一个申请中断请求线的过程。我们一个个来看这几个参数。
handler是一个中断处理函数句柄(一个指针指向中断处理函数)
标记位flags为有以下几种情况:
1.IRQF_DISABLED 关闭所有中断
2.IRQF_SAMPLE_RANDOM 看不懂= =。
3.IRQF_TIMER 指明是系统计时器引起的中断。
4.IRQF_SHARED 中断请求线可以被不同的中断处理函数共享
name是设备名称
dev用于共享中断请求线,当请求的中断请求线是共享的时候,必须设置dev。
request_irq()函数是可阻塞的,因此不能在中断上下文中被调用,原因就是里面有个kmalloc()不知道是从哪里冒粗来的。
void free_irq(unsigned int irq, void *dev)
该函数用于释放中断处理函数,注意参数dev,这就是为什么共享请求线时为什么要设置dev,否则无法区分释放的是同一个irq中的哪个函数。
Writing and Interrupt Handler
主要是一些中断处理函数需要注意的地方。例如中断处理函数是不可重入的,当中断处理函数运行时,相应的中断请求线会被屏蔽,任何处理器都不能使用。
还有一点,当系统收到中断时,它会调用每一个同一中断线共享的每个函数,中断处理函数需要立马判断是否中断是由其对应的设备产生的(通过设备的寄存器),如不是则立刻退出。
中断服务程序中一般使用spin_lock()自旋锁来实现资源互斥。详细后面章节会介绍。
Interrupt Context
中断处理程序运行在中断上下文中,在中断上下文中没有对应的进程,由于没有后备进程,因此中断上下文中不能睡眠(阻塞),不能执行schedule(),可阻塞的函数不能出现在中断处理程序中。中断上下文有它自己独立的中断栈。
Implementing Interrupt Handlers
硬件发送电信号-> 中断控制器 -> 处理器 -> 处理机中断内核 -> 内核调用do_IRQ() -> 判断是否存在相应的中断处理函数 -> 若存在则执行handle_IRQ_event() -> ret_from_intr()返回被中断的内核代码处。
Interrupt Control
1.Disabling and Enabling Interrupts
通过下面两个函数可以关闭和开启当前处理机的中断:
local_irq_diasble();
/* interrupts are disabled .. */
local_irq_enable();