感谢支持
我们一直在努力

Linux中断延迟之tasklet

tasklet是I/O驱动程序中实现可延迟函数的首选方法。从下面的内核代码的分析中我们会看到,tasklet建立在两个叫做HI_SOFTIRQ和TASKLET_SOFTIRQ的软中断之上。几个tasklet可以与同一个软中断相关联,每个tasklet执行自己的函数。tasklet和高优先级的tasklet分别存放在tasklet_vec和tasklet_hi_vec数组中。下面我们结合具体的代码来了解他的实现和运用。


tasklet的内核实现


在start_kernel函数做内核初始化工作的时候会调用函数softirq_init


  1. void __init softirq_init(void)  

  2. {  

  3.     int cpu;  

  4.   

  5.     for_each_possible_cpu(cpu) {  

  6.         int i;  

  7.         /*对tasklet相关pcp变量的初始化*/  

  8.         per_cpu(tasklet_vec, cpu).tail =  

  9.             &per_cpu(tasklet_vec, cpu).head;  

  10.         per_cpu(tasklet_hi_vec, cpu).tail =  

  11.             &per_cpu(tasklet_hi_vec, cpu).head;  

  12.         for (i = 0; i < NR_SOFTIRQS; i++)  

  13.             INIT_LIST_HEAD(&per_cpu(softirq_work_list[i], cpu));  

  14.     }  

  15.   

  16.     register_hotcpu_notifier(&remote_softirq_cpu_notifier);  

  17.     /*将tasklet 执行函数加入软中断向量中, 

  18.     这个执行函数会执行tasklet对应一个链表 

  19.     中的所有函数*/  

  20.     open_softirq(TASKLET_SOFTIRQ, tasklet_action);  

  21.     open_softirq(HI_SOFTIRQ, tasklet_hi_action);  

  22. }  
open_softirq函数在前面我们已经分析过了,在这里可以看出,两类tasklet以一个软中断的方式加入软中断向量中,而这两种tasklet实际上位两个链表,就是上面的tasklet_hi_vec和tasklet_vec,我们看一个就行了,实现大同小异。


  1. static void tasklet_action(struct softirq_action *a)  

  2. {  

  3.     struct tasklet_struct *list;  

  4.   

  5.     local_irq_disable();/*禁用本地中断*/  

  6.     list = __get_cpu_var(tasklet_vec).head;/*将链表的地址保存*/  

  7.     __get_cpu_var(tasklet_vec).head = NULL;/*已调度的tasklet描述符的链表被清空*/  

  8.     __get_cpu_var(tasklet_vec).tail = &__get_cpu_var(tasklet_vec).head;  

  9.     local_irq_enable();/*打开本地中断*/  

  10.   

  11.     while (list) {/*对于list链表的每个tasklet描述符*/  

  12.         struct tasklet_struct *t = list;  

  13.   

  14.         list = list->next;  

  15.   

  16.         if (tasklet_trylock(t)) {  

  17.             /*通过查看tasklet描述符的count字段, 

  18.             检查tasklet是否被禁止*/  

  19.             if (!atomic_read(&t->count)) {/*如果没有禁止*/  

  20.                 /*清楚调度标志*/  

  21.                 if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))  

  22.                     BUG();  

  23.                 t->func(t->data);/*执行对应函数*/  

  24.                 tasklet_unlock(t);  

  25.                 continue;/*继续下一个*/  

  26.             }  

  27.             tasklet_unlock(t);  

  28.         }  

  29.         /*运行到这里,表示tasklet被禁止*/  

  30.         local_irq_disable();  

  31.         t->next = NULL;  

  32.         /*重新插入到链表中,然后再次激活软中断*/  

  33.         *__get_cpu_var(tasklet_vec).tail = t;  

  34.         __get_cpu_var(tasklet_vec).tail = &(t->next);  

  35.         /*这里的激活实际上设置位掩码pending 

  36.         的对应位,使其在软中断时能够被执行*/  

  37.         __raise_softirq_irqoff(TASKLET_SOFTIRQ);  

  38.         local_irq_enable();  

  39.     }  

  40. }  


可以看到,tasklet其实是软中断中两项,每一项对应的不是一个软中断函数,而是一个链表上的所有函数,在对应的软中断到来时,对应链表中的所有函数都将得到执行。而对于tasklet的唤醒其实就是设置pending位掩码的相应位,使软中断到来时会执行他。

tasklet的内核编程与应用

了解了tasklet的内核实现,对于他的应用就很简单了,首先,你应该分配一个新的tasklet_struct数据结构,并调用tasklet_init函数初始化它。


  1. void tasklet_init(struct tasklet_struct *t,  

  2.           void (*func)(unsigned long), unsigned long data)  

  3. {  

  4.     t->next = NULL;  

  5.     t->state = 0;  

  6.     atomic_set(&t->count, 0);  

  7.     t->func = func;  

  8.     t->data = data;  

  9. }  
为了重新激活你的tasklet,调用tasklet_enable函数;


为了激活tasklet,根据自己tasklet需要的优先级,调用tasklet_schedule函数或tasklet_hi_schedule函数。我们也只看一个


  1. static inline void tasklet_hi_schedule(struct tasklet_struct *t)  

  2. {  

  3.     /*标志位的检查*/  

  4.     if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))  

  5.         __tasklet_hi_schedule(t);  

  6. }  

 


  1. void __tasklet_hi_schedule(struct tasklet_struct *t)  

  2. {  

  3.     unsigned long flags;  

  4.   

  5.     local_irq_save(flags);  

  6.     t->next = NULL;  

  7.     *__get_cpu_var(tasklet_hi_vec).tail = t;  

  8.     __get_cpu_var(tasklet_hi_vec).tail = &(t->next);  

  9.     /*激活对应softirq_vec[]数组中HI_SOFTIRQ下标 

  10.     的软中断,就是tasklet_hi_vec链表*/  

  11.     raise_softirq_irqoff(HI_SOFTIRQ);  

  12.     local_irq_restore(flags);  

  13. }  
实现了上面的几个操作流程,当软中断函数一旦被唤醒就由do_softirq函数来执行。

赞(0) 打赏
转载请注明出处:服务器评测 » Linux中断延迟之tasklet
分享到: 更多 (0)

听说打赏我的人,都进福布斯排行榜啦!

支付宝扫一扫打赏

微信扫一扫打赏