中断的概念:用于CPU与外设之间信息交互的机制。
为什么需要中断?
1、外设的处理速度一般慢于CPU;
2、CPU不能一直等待外部事件
所以设备必须有一种方法来通知CPU它的工作进度,这种方法就是中断。
中断的实现:
1、向内核注册中断;
2、实现中断处理函数。
中断注册:
request_irq用于实现中断的注册功能 :
int request_irq(unsigned int irq, //中断号
void (*handler)(int, void *, struct pt_regs *), //中断处理程序
unsigned long flags, //中断处理标志,与中断管理有关的各种选项
const char *devname, //设备名
void *dev_id) //共享中断时使用
返回0表示成功,或者返回一个错误码。
flags:
可以选择一些与中断管理有关的选项:
IRQF_DISABLE(SA_INTERRUPT)
如果设置该位,表示是一个“快速”中断处理程序;
快速:保证中断处理的原子性(不被打断)。“开启中断”标志位(处理器IF)在运行快速中断处理程序时是关闭的,因此在服务该中断时,不会被其他类型的中断打断。不允许中断嵌套。
慢速:不保证中断处理的原子性。默认情况下,都是慢速中断。
IRQF_SHARED(SA_SHIRQ)
表明中断可以在设备间共享。
共享中断:将不同的设备挂到同一个中断信号线上。Linux对共享的支持主要是为PCI设备服务。
1、申请共享中断时,必须在flags参数中指定IRQF_SHARED位。
2、dev_id参数必须是唯一的。
why?在释放中断时候,会要求输入dev_id。如何做到唯一?
3、共享中断的处理程序中,不能使用disable_irq(unsigned int irq);
作用:关闭中断号irq所对应的中断。如果使用了这个函数,共享中断信号线的其它设备将同样无法使用中断,也就无法正常工作了。
IRQ_TYPE_EDGE_FALLING:下降沿中断
IRQ_TYPE_EDGE_RISING:上升沿中断
IRQ_TYPE_EDGE_BOTH: 两者都中断
释放中断:
当设备不再需要使用中断时(通常在驱动卸载时),应当把他们返还给系统:
void free_irq(unsigned int irq, void *dev_id);
中断处理程序:
中断处理程序就是普通的C代码。特别之处在于中断处理程序时在中断上下文中运行的,行为会受到某些限制:
1、不能向用户空间发送或接受数据
用户空间和进程对应,中断处理程序是属于中断上下文的,不对应任何进程。
2、不能使用可能引起阻塞的函数
阻塞函数是用于阻塞线程和进程的,所以不能使用
3、不能使用可能引起调度的函数
没有进程,无法调度
实现流程:
void short_sh_interrupt(int irq, void *dev_id, struct pt_regs *regs);
/*判断是否是本设备产生了中断(为什么要做这样的检测?)*/
/*因为共享中断的原因,内核收到中断以后,会去把中断线上的所有中断处理程序调用一遍,所以在处理的时候要做检查*/
value = inb(short_base); //short_base中断标志寄存器
if(!(value & 0x80)) return;
/*清除中断标志位(如果设备支持自动清除,则不需要这步)*/
outb(value & 0x7f, short_base);
/*中断处理,通常是数据接收等等。。*/
...
/*唤醒等待数据的进程(等待数据的时候发生的阻塞)*/
wake_up_interruptible(&short_queue);