1.1 中断的分类
1.根据中断来源,中断可分为内部中断和外部中断
内部中断:中断源来自CPU内部,如软件中断指令、溢出、除法错误等
外部中断:中断源来自CPU外部,由外设提出请求
2.根据中断是否可以屏蔽,分为可屏蔽中断和不可屏蔽中断(NMI)
可屏蔽中断可以通过屏蔽字被屏蔽,屏蔽后,该中断不再得到响应,而不屏蔽
中断不能被屏蔽
3.根据中断入口跳转方法的不同,分为向量中断和非向量中断
向量中断:采用向量中断的CPU通常为不同的中断分配不同的中断号,当检测到某中断号的中断到来后,就自动跳转到该中断号对应的地址执行,不同中断号的中断有不同的入口地址
非向量中断:非向量中断的多个中断共享一个入口地址,进入入口地址后再通过软件判断中断标志来识别具体是哪个中断
向量中断由硬件提供中断服务程序入口地址,非向量中断由软件提供中断服务程序入口地址
1.2 Linux中断处理程序架构
1.中断分成上半部与底半部的原因
设备的中断会打断内核中进程的正常运行,系统对更高吞吐率的追求要求中断服务程序尽可能短小精悍,然而在大多数系统中,当中断到来时,要完成的工作并不是短小的,它可能要进行大量的耗时处理,为了在中断执行时间尽可能短和中断处理需完成大量工作之间找到一个平衡点,LINUX将中断处理程序分解成两个半部:顶半部(top half)和底半部(bottom half)
2.上半部和底半部实现方法
顶半部完成尽可能少的比较紧急的功能,它往往只是简单地读取寄存器中的中断状态并清除中断标志后就进行“登记中断”的工作。“登记中断”意味着将底半部处理程序挂到该设备的底半部执行队列中去。这样,顶半部执行的速度就会很快,可以服务更多的中断请求
中断处理的工作重心就落在底半部上了,它完成中断事件的绝大多数任务,而且可以被新的中断打断,这也是底半部和顶半部最大的不同,因为顶半部往往被设计成不可中断
如果中断要处理的工作本身很少,则完全可以直接在顶半部完成
1.3 Linux中断编程
1.申请和释放中断
1)申请IRQ--- request_irq
int request_irq(unsigned int irq, irq_handler_t handler, unsigned
long irqflags, const char *devname, void *dev_id)
irq:要申请的硬件中断号
handler:向系统登记的中断处理函数(顶半部),是一个回调函数,中断发生时
系统调用这个函数,dev_id参数将被传递给它
irqflags:中断处理的属性
IRQF_DISABLED:表明中断处理程序是快速处理程序
IRQF_SHARED:表示多个设备共享中断
dev_id:在中断共享时会用到,一般设置为这个设备的结构体或者NULL
返回值:0--表示成功 -EINVAL--表示叫断号无效 -EBUSY--表示中断号已经被
占用且不能共享
2)释放IRQ--- free_irq
void free_irq(unsigned int irq, void *dev_id)
2.使能和屏蔽中断
用于屏蔽一个中断源:void disable_irq(int irq);
void disable_irq_nosync(int irq);
void enable_irq(int irq);
disable_irq()和disable_irq_nosync()的区别:前者立即返回,后者等待目
前的中断处理完成
用于屏蔽本CPU内的所有中断:#define local_irq_save(flags)....
void local_irq_disable(void);
与上面两个禁止中断对应的恢复中断的函数:
#define local_irq_restore(flags)....
void local_irq_enable(void);
3.底半部机制
linux实现底半部的机制主要有tasklet、工作队列和软中断
1)tasklet
只需要定义tasklet及其处理函数并将两者关联,比如:
void xxx_do_tasklet (unsigned long); //定义一个中断底半部处理函数
DECLARE_TASKLET(xxx_tasklet, xxx_do_tasklet,data); //将两者相关联
在需要调度时tasklet时,引用一个tasklet_schedule()函数就能使系统在适当的时候进行调度运行,如tasklet_schedule(&xxx_tasklet)
2)工作队列
步骤:
1.定义一个工作队列
struct work_struct xxx_wq;
2.定义一个处理函数
void xxx_do_work(unsigned long);
3.通过INIT_WORK()初始化这个工作队列并将工作队列与处理函数绑定
INIT_WORK(&xxx_wq, (void (*)(void *)) xxx_do_work, NULL);
此函数在模块初始化中完成(xxx_init中)
4.调度工作队列执行:
schedule_work(&xxx_wq);
中断处理顶半部中完成,也就是xxx_interrupt函数中
3)软中断
open_softirq():用于注册软中断对应的处理函数
raise_softirq():触发一个软中断
软中断和tasklet运行于软中断的上下文,仍然属于原子上下文的一种,而工作队列运行于进程上下文。因此,软中断和tasklet处理函数中不能睡眠,而工作队列处理函数中允许睡眠
local_hb_disable()和local_hb_enable()是内核中用于禁止和全能软中断和tasklet底半部机制的函数
硬中断、软中断和信号的区别:
硬中断是外部设备对CPU的中断,软中断是中断底半部的一种处理机制,而信号是由内核对某个进程的中断