内核中断过程
为什么需要中断?
1)外设的处理速度一般慢于CPU
2)CPU不能一直等外部事件
所以设备必须有一种方法来通知CPU它的工作进度,这就是中断。
步骤:
1)向内核注册中断
2)实现中断处理函数
CPU如何识别中断:
在Intel X86中可以支持256中向量中断,为了使处理器能使别每种中断源,给它们进行了编号----->叫做中断向量
这些中断向量在Linux中的分配:
1)编号0~31的向量对应于异常和非屏蔽中断
2)编号32~47的向量(即由IO设备引起的中断)分配给屏蔽中断。
3)编号48~255的向量用来标示软中断。
Linux用其中的128或0x80来实现系统调用, 非屏蔽中断的向量和异常的向量是固定的。
异常和中断的区别:
1)异常:是指CPU内部出现的中断,即在CPU执行特定指令时出现的非法情况。同时异常也称为同步中断,因此只有在一条指令执行后才会发出中断 ,不可能在指令执行期间发生异常。
a.产生的原因:
程序的错误产生的(eg:除数为0)
内核必须处理的异常条件产生的(eg:缺页)
b.异常又分为故障和陷阱,它们都不使用中断控制器,也不能被屏蔽
c. X86处理处理器中大约有20中异常。Linux内核必须为每种异常提供一个专门的异常处理程序。
2)中断:也称为异步中断。因此它是由其他硬件设备依照 CPU 时钟信号随机产生,即意味着中断能在指令之间发生。
a.中断又分为外部可屏蔽中断(INTR)和外部非屏蔽中断(NMI)
所用I0设备产生的中断请求均引起可屏蔽中断
硬件故障引起的故障则产生非屏蔽中断。
说明:
在CPU执行一个异常处理程序时,就不再响应其他异常和中断请求服务.那么如果此时发生了一个异常,CPU不能去响应它,又不能把它的信息丢失该怎么办呢?
这是就用到了堆栈,把所有的信息压入栈。等当前异常处理后,才从堆栈中取出信息再响应刚才的异常。(当产生多个非屏蔽中断时,CPU的处理方法同上)
int request_irq ( unsigned int irq, void (*handler)(int, void*, struct pt_regs *)
, unsigned long flags , const char *devname , void *dev_id )
返回0表示成功,或者返回一个错误码
参数:
irq:中断号
handler:中断处理函数 指针
flags:与中断管理有关的各种选项
devname:设备名
dev_id:共享中断时使用
在flags参数中,可以选择一些与中断管理有关的选项
如:
IRQF_DISABLED(SA_INTERRUPT)
如果设置该位,表示是一个“快速”中断处理程序
如果没有,表示是一个“慢速”中断处理程序
IRQF_SHARED(SA_SHIRQ)
该位表示中断可以在设备间共享
快速中断不允许中断嵌套(不被打断)
慢速中断可以中断嵌套,其它类型的中断可以得到服务(默认)
共享中断就是将不同的设备挂到同一个中断信号线上。
Linux对共享的支持主要是为PCI设备服务
共享中断也是通过request_irq函数来注册的,但有三个特别之处:
1)申请共享中断时,必须在flags参数中指定IRQF_SHARED位
2)dev_id参数必须是唯一的
为什么要唯一?
在释放中断时要void free_irq()不然内核不知道你要释放哪个设备(共享中断时有不只一个设备在用这个中断)
3)不能使用disable_irq(unsigned int irq)(这个函数是禁止中断的)
为什么?
如果使用了,共享中断信号线的设备同样无法使用中断,也就是无法正常工作了。
中断处理程序是中断上下文件中运行的,所以它的行为受到某些限制:
1)不能向用户空间发送或者接受数据
2)不能使用可能引起阻塞的函数
3)不能使用可能引起调度的函数
中断函数实现流程: void short_sh_interrupt(int irq, void *dev_id, struct pt_regs *regs) { /*判断是否是本设备产生了中断*/ value = inb(short_base); if (!(value & 0x80)) return; /*清除中断位(如设备支持自动清除,则不需要这步) */ outb(value & 0x7f, short_base); /*中断处理,通常是数据接收*/ ......... /*唤醒等待数据的进程*/ wake_up_interruptible(&short_queue); } 释放中断 void free_irq(unsigned int irq, void *dev_id);