Linux提供了很多API函数用于中断编程,同时提供了几种底半部机制用于中断函数的实现。正确的使用它们,有利于优化需要实现的中断机制。 .1 申请IRQstatic inline int __must_check request_irq(unsigned int irq, irq_handler_thandler, unsigned long flags,const char *name, void *dev) { returnrequest_threaded_irq(irq, handler, NULL, flags, name, dev); } irq为要申请的硬件中断号,handle为向系统登记的中断处理函数,中断发生时系统将调用它。flags为中断处理的属性。 request_irq()返回0表示申请成功,返回-INVAL表示中断号无效或处理函数指针为NULL,返回-EBUSY表示中断已经被占用且不能共享。 .2 释放IRQvoid free_irq(unsigned int irq, void *dev_id) { kfree(__free_irq(irq,dev_id)); } 它与request_irq()函数对应,用于释放IRQ中断。 .3 使能和屏蔽中断void disable_irq(unsigned int irq) { structirq_desc *desc = irq_to_desc(irq); if(!desc) return; disable_irq_nosync(irq); if(desc->action) synchronize_irq(irq); } disable_irq函数用于屏蔽IRQ中断; void enable_irq(unsigned int irq) { structirq_desc *desc = irq_to_desc(irq); unsignedlong flags; if(!desc) return; spin_lock_irqsave(&desc->lock,flags); __enable_irq(desc,irq, false); spin_unlock_irqrestore(&desc->lock,flags); } enable_irq函数用于使能IRQ中断,可以看到函数中加入了自旋锁。 #define local_irq_enable() \ do {trace_hardirqs_on(); raw_local_irq_enable(); } while (0) #define local_irq_disable() \ do {raw_local_irq_disable(); trace_hardirqs_off(); } while (0) #define local_irq_save(flags) \ do { \ typecheck(unsignedlong, flags); \ raw_local_irq_save(flags); \ trace_hardirqs_off(); \ }while (0) #define local_irq_restore(flags) \ do { \ typecheck(unsignedlong, flags); \ if(raw_irqs_disabled_flags(flags)) { \ raw_local_irq_restore(flags); \ trace_hardirqs_off(); \ }else { \ trace_hardirqs_on(); \ raw_local_irq_restore(flags); \ } \ }while (0) local_irq_enable和local_irq_restore函数用于恢复中断,local_irq_disable和local_irq_save用于屏蔽所有中断。local_irq_save将目前的中断状态保留在flags中,而local_irq_disable直接禁止中断。这里的local表明作用范围在本CPU内。 .4 底半部机制 Linux实现底半部中断的机制主要有tasklet,工作队列和软中断。通常我们使用前两种。 .4.1 tasklet定义tasklet及其处理函数,并将两者关联: void my_tasklet_func(unsigned long); DECLARE_TASKLET(my_tasklet,my_tasklet_func,data); DECLARE_TASKLET定义名称为my_tasklet的tasklet并将其与my_tasklet_func函数绑定,传入这个函数的参数为data。在需要调度tasklet的时候引用tasklet_schedule函数就能使系统在适当时候运行: tasklet_schedule(&my_tasklet); .4.2 工作队列
|