tasklet

Linux的中断处理框架大体为:入口点-->driver安装的中断处理函数-->退出点
通常说Linux下的中断处理分为top和bottom两部分,top部分中断是关闭的,因此一般在这里的代码都会以迅雷不及掩耳盗铃之势做完该做的事,然后打开中断以防止可能的中断丢失。因为要迅雷不及掩耳盗铃,所以大部分耗时的任务交给了bottom部分去处理。bottom阶段中断是打开的,所以有可能被其他中断到来时所打断。
bottom的延后处理,其中之一遍是由tasklet来完成,相对于workqueue这种任务延后机制,tasklet实际上是处在中断上下文中,因为处在空灵世界(不属于任一进程),所以在tasklet中休眠是绝对应该禁止的。
其他的一些bottom方法:
/* PLEASE, avoid to allocate new softirqs, if you need not _really_ high
   frequency threaded job scheduling. For almost all the purposes
   tasklets are more than enough. F.e. all serial device BHs et
   al. should be converted to tasklets, not to softirqs.
*/

enum
{
        HI_SOFTIRQ=0,
        TIMER_SOFTIRQ,
        NET_TX_SOFTIRQ,
        NET_RX_SOFTIRQ,
        BLOCK_SOFTIRQ,
        TASKLET_SOFTIRQ,        
        SCHED_SOFTIRQ,
        HRTIMER_SOFTIRQ,
        RCU_SOFTIRQ,        /* Preferable RCU should always be the last softirq */

        NR_SOFTIRQS
};

看了tasklet的大体实现,很容易想到内核中另一个延迟执行机制:workqueque


如果摆渡或者股沟一下,都会发现它们之间如下的区别:
1. tasklet在中断上下文中执行,所以tasklet中的代码不应该导致调度点的产生(如果调度,因为当前代码不属于任何一进程,或者current此时无效,tasklet在一个边缘世界里).而工作线程是在一个内核进程中,所以工作队列的函数当然可以睡眠。
2.tasklet始终运行在被当初提交它的那个CPU上,但对于工作队列而言,这只是它的缺省行为,当然可以改变它。
3.内核代码可以请求工作队列函数的执行延迟给定的时间间隔。

我们说,深入内核代码的最终目的,一是学习内核代码的设计思想,另一个重要目的是在写我们自己的代码时,因为能清楚了解各种内核设施的背后机制,因而可以能选择最佳的解决方案:你应该选择tasklet还是workqueue。

在海豚看来,在绝大多数的实际使用中,tasklet和workqueue之间的后两点区别并不重要,因为不管在哪个CPU上运行,你的代码总是会被执行。除非你的项目要非常精确地对性能进行调整,此外给请求队列函数的执行指定给定的时间间隔现实中也很少用到。

它们之间最关键的区别应该是第一点,这点决定了tasklet的代码应该是短小精悍,而且对系统的开销也小(无需象workqueue那样生成一个新的进程),并且它不能睡眠。workqueue则正好相反。

海豚曾经遇到过在tasklet中调用一个对外围spi设备寄存器读写函数,类似spi_reg_read/write云云,如果在tasklet中调用这个函数,系统必然crash掉。因为spi_reg_read/write的内部实现使用了中断驱动,阻塞I/O的实现机制,因此睡眠是必不可少的。此种情况,你别无选择,只能使用workqueue!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是一个简单的 Linux tasklet 示例代码: ``` #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/interrupt.h> #define SHARED_IRQ 19 static int irq = SHARED_IRQ, my_dev_id, irq_counter = 0; module_param (irq,int,0664); void tasklet_handler(unsigned long data) { printk(KERN_INFO "In the tasklet handler function\n"); } DECLARE_TASKLET(my_tasklet, tasklet_handler, 0); static irqreturn_t my_interrupt(int irq, void *dev_id) { irq_counter++; printk(KERN_INFO "In the ISR: counter = %d\n", irq_counter); tasklet_schedule(&my_tasklet); return IRQ_HANDLED; } static int __init my_tasklet_init(void) { if (request_irq(irq, my_interrupt, IRQF_SHARED, "my_interrupt", &my_dev_id)) return -1; printk(KERN_INFO "Successfully loaded the tasklet module\n"); return 0; } static void __exit my_tasklet_exit(void) { tasklet_kill(&my_tasklet); free_irq(irq, &my_dev_id); printk(KERN_INFO "Successfully removed the tasklet module\n"); } module_init(my_tasklet_init); module_exit(my_tasklet_exit); MODULE_AUTHOR("TechBeamers"); MODULE_DESCRIPTION("Tasklet Example Module"); MODULE_LICENSE("GPL"); ``` 在此示例中,我们首先声明了一个名为“my_tasklet”的任务,在其中定义了一个称为“tasklet_handler”的函数,当任务激活时将调用此函数。然后我们使用“DECLARE_TASKLET”宏将任务声明为全局。 我们还定义了一个中断处理程序(“my_interrupt”),它会增加一个计数器并调度任务。最后,我们还为模块提供了一个加载和卸载函数,实现请求和释放共享中断,并在系统日志中显示状态消息。 希望对你有所帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值