歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux教程 >> Linux中斷延遲之tasklet

Linux中斷延遲之tasklet

日期:2017/2/28 16:00:37   编辑:Linux教程

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. for_each_possible_cpu(cpu) {
  5. int i;
  6. /*對tasklet相關pcp變量的初始化*/
  7. per_cpu(tasklet_vec, cpu).tail =
  8. &per_cpu(tasklet_vec, cpu).head;
  9. per_cpu(tasklet_hi_vec, cpu).tail =
  10. &per_cpu(tasklet_hi_vec, cpu).head;
  11. for (i = 0; i < NR_SOFTIRQS; i++)
  12. INIT_LIST_HEAD(&per_cpu(softirq_work_list[i], cpu));
  13. }
  14. register_hotcpu_notifier(&remote_softirq_cpu_notifier);
  15. /*將tasklet 執行函數加入軟中斷向量中,
  16. 這個執行函數會執行tasklet對應一個鏈表
  17. 中的所有函數*/
  18. open_softirq(TASKLET_SOFTIRQ, tasklet_action);
  19. open_softirq(HI_SOFTIRQ, tasklet_hi_action);
  20. }
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. local_irq_disable();/*禁用本地中斷*/
  5. list = __get_cpu_var(tasklet_vec).head;/*將鏈表的地址保存*/
  6. __get_cpu_var(tasklet_vec).head = NULL;/*已調度的tasklet描述符的鏈表被清空*/
  7. __get_cpu_var(tasklet_vec).tail = &__get_cpu_var(tasklet_vec).head;
  8. local_irq_enable();/*打開本地中斷*/
  9. while (list) {/*對於list鏈表的每個tasklet描述符*/
  10. struct tasklet_struct *t = list;
  11. list = list->next;
  12. if (tasklet_trylock(t)) {
  13. /*通過查看tasklet描述符的count字段,
  14. 檢查tasklet是否被禁止*/
  15. if (!atomic_read(&t->count)) {/*如果沒有禁止*/
  16. /*清楚調度標志*/
  17. if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))
  18. BUG();
  19. t->func(t->data);/*執行對應函數*/
  20. tasklet_unlock(t);
  21. continue;/*繼續下一個*/
  22. }
  23. tasklet_unlock(t);
  24. }
  25. /*運行到這裡,表示tasklet被禁止*/
  26. local_irq_disable();
  27. t->next = NULL;
  28. /*重新插入到鏈表中,然後再次激活軟中斷*/
  29. *__get_cpu_var(tasklet_vec).tail = t;
  30. __get_cpu_var(tasklet_vec).tail = &(t->next);
  31. /*這裡的激活實際上設置位掩碼pending
  32. 的對應位,使其在軟中斷時能夠被執行*/
  33. __raise_softirq_irqoff(TASKLET_SOFTIRQ);
  34. local_irq_enable();
  35. }
  36. }

可以看到,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. local_irq_save(flags);
  5. t->next = NULL;
  6. *__get_cpu_var(tasklet_hi_vec).tail = t;
  7. __get_cpu_var(tasklet_hi_vec).tail = &(t->next);
  8. /*激活對應softirq_vec[]數組中HI_SOFTIRQ下標
  9. 的軟中斷,就是tasklet_hi_vec鏈表*/
  10. raise_softirq_irqoff(HI_SOFTIRQ);
  11. local_irq_restore(flags);
  12. }
實現了上面的幾個操作流程,當軟中斷函數一旦被喚醒就由do_softirq函數來執行。
Copyright © Linux教程網 All Rights Reserved