- 论坛徽章:
- 0
|
linux中断延迟之tasklet
tasklet是I/O驱动程序中实现可延迟函数的首选方法。从下面的内核代码的分析中我们会看到,tasklet建立在两个叫做HI_SOFTIRQ和TASKLET_SOFTIRQ的软中断之上。几个tasklet可以与同一个软中断相关联,每个tasklet执行自己的函数。tasklet和高优先级的tasklet分别存放在tasklet_vec和tasklet_hi_vec数组中。下面我们结合具体的代码来了解他的实现和运用。
tasklet的内核实现
在start_kernel函数做内核初始化工作的时候会调用函数softirq_init
view plaincopy to clipboard- 01.void __init softirq_init(void)
- 02.{
- 03. int cpu;
- 04.
- 05. for_each_possible_cpu(cpu) {
- 06. int i;
- 07. /*对tasklet相关pcp变量的初始化*/
- 08. per_cpu(tasklet_vec, cpu).tail =
- 09. &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,我们看一个就行了,实现大同小异。
view plaincopy to clipboard- 01.static void tasklet_action(struct softirq_action *a)
- 02.{
- 03. struct tasklet_struct *list;
- 04.
- 05. local_irq_disable();/*禁用本地中断*/
- 06. list = __get_cpu_var(tasklet_vec).head;/*将链表的地址保存*/
- 07. __get_cpu_var(tasklet_vec).head = NULL;/*已调度的tasklet描述符的链表被清空*/
- 08. __get_cpu_var(tasklet_vec).tail = &__get_cpu_var(tasklet_vec).head;
- 09. 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函数初始化它。
view plaincopy to clipboard- 01.void tasklet_init(struct tasklet_struct *t,
- 02. void (*func)(unsigned long), unsigned long data)
- 03.{
- 04. t->next = NULL;
- 05. t->state = 0;
- 06. atomic_set(&t->count, 0);
- 07. t->func = func;
- 08. t->data = data;
- 09.}
复制代码 为了重新激活你的tasklet,调用tasklet_enable函数;
为了激活tasklet,根据自己tasklet需要的优先级,调用tasklet_schedule函数或tasklet_hi_schedule函数。我们也只看一个
view plaincopy to clipboard- 01.static inline void tasklet_hi_schedule(struct tasklet_struct *t)
- 02.{
- 03. /*标志位的检查*/
- 04. if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
- 05. __tasklet_hi_schedule(t);
- 06.}
复制代码 view plaincopy to clipboard- 01.void __tasklet_hi_schedule(struct tasklet_struct *t)
- 02.{
- 03. unsigned long flags;
- 04.
- 05. local_irq_save(flags);
- 06. t->next = NULL;
- 07. *__get_cpu_var(tasklet_hi_vec).tail = t;
- 08. __get_cpu_var(tasklet_hi_vec).tail = &(t->next);
- 09. /*激活对应softirq_vec[]数组中HI_SOFTIRQ下标
- 10. 的软中断,就是tasklet_hi_vec链表*/
- 11. raise_softirq_irqoff(HI_SOFTIRQ);
- 12. local_irq_restore(flags);
- 13.}
复制代码 实现了上面的几个操作流程,当软中断函数一旦被唤醒就由do_softirq函数来执行。
|
|