歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> Linux教程

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函數來執行。
Copyright © Linux教程網 All Rights Reserved