/*定义 tasklet 和底半部函数并关联*/ void xxx_do_tasklet(unsigned long); DECLARE_TASKLET(xxx_tasklet, xxx_do_tasklet, 0); /*中断处理底半部*/ void xxx_do_tasklet(unsigned long) { ... } /*中断处理顶半部*/ irqreturn_t xxx_interrupt(int irq, void *dev_id, struct pt_regs *regs) { ... tasklet_schedule(&xxx_tasklet); ... } /*设备驱动模块加载函数*/ int __init xxx_init(void) { ... /*申请中断*/ result = request_irq(xxx_irq, xxx_interrupt, SA_INTERRUPT, "xxx", NULL); ... } /*设备驱动模块卸载函数*/ void __exit xxx_exit(void) { ... /*释放中断*/ free_irq(xxx_irq, xxx_interrupt); ... }
/*定义工作队列和关联函数*/ struct work_struct xxx_wq; void xxx_do_work(unsigned long); /*中断处理底半部*/ void xxx_do_work(unsigned long) { ... } /*中断处理顶半部*/ irqreturn_t xxx_interrupt(int irq, void *dev_id, struct pt_regs *regs) { ... schedule_work(&xxx_wq); ... } /*设备驱动模块加载函数*/ int xxx_init(void) { ... /*申请中断*/ result = request_irq(xxx_irq, xxx_interrupt, SA_INTERRUPT, "xxx", NULL); ... /*初始化工作队列*/ INIT_WORK(&xxx_wq, (void (*)(void *)) xxx_do_work, NULL); ... } /*设备驱动模块卸载函数*/ void xxx_exit(void) { ... /*释放中断*/ free_irq(xxx_irq, xxx_interrupt); ... }
尽管 Linux 专家们多建议在设备第一次打开时才申请设备的中断并在最后一次关闭时释放中断以尽量减少中断被这个设备占用的时间,但是,大多数情况下,为求省事, 大多数驱动工程师还是将中断申请和释放的工作放在了设备驱动的模块加载和卸载函数中。
另转:
tasklet特性: 1.一个tasklet可在稍后被禁止或者重新启用;只有启用的次数和禁止的次数相同时,tasklet才会被执行。
2.和定时器类似,tasklet可以自己注册自己。 3.tasklet可被调度以在通常的优先级或者高优先级执行。高优先级的tasklet总会优先执行。 4.如果系统负荷不重,则tasklet会立即执行,但始终不会晚于下一个定时器滴答 5.一个tasklet可以和其它tasklet并发,但对自身来讲是严格串行处理的,也就是说,同一tasklet永远不会在多个处理器上同时运行:tasklet始终会调度自己在同一CPU上运行; 工作队列: 表面来看,工作队列类似于tasklet:允许内核代码请求某个函数在将来的时间被调用。 但其实还是有很多不同: 1.tasklet在软中断上下文中运行,因此,所有的tasklet代码都是原子的。相反,工作队列函数在一个特殊的内核进程上下文中运行,因此他们有更好的灵活性 尤其是,工作队列可以休眠! 2.tasklet始终运行在被初始提交的统一处理器上,但这只是工作队列的默认方式 3.内核代码可以请求工作队列函数的执行延迟给定的时间间隔 4.tasklet 执行的很快, 短时期, 并且在原子态, 而工作队列函数可能是长周期且不需要是原子的,两个机制有它适合的情形。
两者的关键区别:tasklet会在很短的时间内很快执行,并且以原子模式执行,而工作队列函数可以具有更长的延迟并且不必原子化。两种机制有各自适合的情形。 更多详见 http://blog.csdn.net/houxn22/article/details/45720247
3.软中断
软中断是用软件方式模拟硬件中断的念,实现宏观上的异步执行效果,tasklet也是基于软中断实现的。