前一篇博客解密了中断系统,下面我介绍一下如何编写中断服务程序。
中断处理系统结构
irqaction数据结构中包含了对应于此中断处理的相关信息,包括中断处理例程的地址,此中断所属的模块名称,以及是否允许共享的标志位,如果允许共享next成员将指向共巷此中断号的下一个irqaction的结构指针等。
中断发生时Linux首先读取系统可编程中断控制器中中断状态寄存器判断出中断源,将其转换成irq_action数组中偏移值。
根据中断号找到相应的irqaction后,内核是凭借 handler指针调入驱动程序的ISR,在此使用循环的意义是如果此中断号被多个ISR共享,可以依次遍历每一个注册的ISR,使它得到一次执行的机会。
注册中断处理函数
int request_irq(unsigned
int irq, void (*handler)(int irq,void dev_id,struct pt_regs *regs), unsigned long flags, const char *device, void *dev_id)
参数irq表示所要申请的硬件中断号。Handler是向系统登记的中断处理子程序,中断产生时由系统来调用,调用时所带参数irq为中断号,dev_id为申请时告诉系统的设备标识,regs为中断发生时寄存器内容。device为设备名,将会出现在/proc/interrupts文件里。flag是申请时的选项,它决定中断处理程序的一些特性,其中最重要的是中断处理程序是快速处理程序(flag里设置了SA_INTERRUPT)还是慢速处理程序(不设置SA_INTERRUPT),快速处理程序运行时,所有中断都被屏蔽,而慢速处理程序运行时,除了正在处理的中断外,其它中断都没有被屏蔽。
Linux内核中注销一个中断处理的函数:
void free_irq(unsigned
int irq, void *dev_id)
中断标志flags
中断标志flags可以设置为:
SA_INTERRUPT。如果设置该位,就指示这是一个"快速"中断处理程序,如果清除这位,那么它就是一个"慢速"中断处理程序。
SA_SHIRQ 该位表明中断可以在设备间共享。共享的概念在稍后的"中断共享"一节中介绍。
SA_SAMPLE_RANDOM 该位表明产生的中断对/dev/random和/dev/urandom设备要使用的熵池(entropy
pool)有贡献。读这些设备返回真正的随机数,它们用来帮助应用软件选取用于加密的安全钥匙。这些随机数是从一个熵池中取得的,各种随机事件都会对系统的熵池(无序度)有贡献。如果希望设备真正随机地产生中断,应该置上这个标志。而如果的中断是可预测的(例如,帧捕捉卡的垂直消隐),那就不值得设置这个标志位-它对系统的熵池没有任何贡献。
ISR上下文
当进程发出一个系统调用的请求时,由应用态切换到内核态。这样的内核控制路径被成为进程内核路径,也叫进程上下文。当CPU执行一个与中断有关的内核控制路径的时候,被成为中断上下文。中断的上半部和下半部都属于ISR上下文。
tasklet是一种特殊的软中断机制,它可以被多次调度运行。Linux内核使用tasklet机制实现底半部处理。软中断向量HI_SOFTIRQ和TASKLET_SOFTIRQ均是用tasklet机制来实现的。实际上,现在的底半处理程序本身就是用tasklet实现的。
tasklet机制是Linux内核对BH机制的一种扩展:
1. 与一般的软中断不同,某一段tasklet代码在某个时刻只能在一个CPU上运行,而不像一般的软中断服务函数(即softirq_action结构中的action函数指针)那样——在同一时刻可以被多个CPU并发地执行。
2. 与BH机制不同,不同的tasklet代码在同一时刻可以在多个CPU上并发地执行,而不像BH机制那样必须严格地串行化执行(也即在同一时刻系统中只能有一个CPU执行BH函数)。
tasklet_struct结构定义在<linux/interrupt.h>文件中
定义一个处理函数:void
my_tasklet_func(unsigned long)
DECLARE_TASKLET(my_tasklet,my_tasklet_func,data);
/*定义一个tasklet结构my_tasklet,与my_tasklet_func(data)函数相关联,相当于DECLARE_TASK_QUEUE()
*/
tasklet_schedule(&my_tasklet);
/* 登记my_tasklet,允许系统在适当的时候进行调度运行,相当于queue_task(&my_task,&tq_immediate)和mark_bh(IMMEDIATE_BH) */
就写这么多吧,快放假了,大家假期快乐。