精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

linux驅動程序下的tasklet機制

運維 系統運維
在編寫設備驅動時,tasklet機制是一種比較常見的機制。在老版本的linux中,通常中斷處理分為top half handler 、bottom half handler。但是在linux2.6以后的linux采取了另外一種機制,即軟機制來代替bottom half handler 的處理。本文主要介紹了tasklet機制在linux中的實現。

       Tasklet 機制 是一種較為特殊的軟中斷。Tasklet一詞的原意是“小片任務”的意思,這里是指一小段可執行的代碼,且通常以函數的形式出現。軟中斷向量HI_SOFTIRQ和TASKLET_SOFTIRQ均是用tasklet機制來實現的。

      從某種程度上講,tasklet機制是Linux內核對BH機制的一種擴展。在2.4內核引入了softirq機制后,原有的BH機制正是通過tasklet機制這個橋梁來納入softirq機制的整體框架中的。正是由于這種歷史的延伸關系,使得tasklet機制與一般意義上的軟中斷有所不同,而呈現出以下兩個顯著的特點:

       1. 與一般的軟中斷不同,某一段tasklet代碼在某個時刻只能在一個CPU上運行,而不像一般的軟中斷服務函數(即softirq_action結構中的action函數指針)那樣——在同一時刻可以被多個CPU并發地執行。

       2. 與BH機制不同,不同的tasklet代碼在同一時刻可以在多個CPU上并發地執行,而不像BH(Bottom Half)機制那樣必須嚴格地串行化執行(也即在同一時刻系統中只能有一個CPU執行BH函數)。

       Linux用數據結構 tasklet_struct 來描述一個tasklet。該數據結構定義在include/linux/interrupt.h頭文件中。如下所示:

 

  1. view sourceprint?1 struct tasklet_struct   
  2.  
  3.  {   
  4.  
  5.  struct tasklet_struct *next;   
  6.  
  7.  unsigned long state;   
  8.  
  9.  atomic_t count;   
  10.  
  11.  void (*func)(unsigned long);   
  12.  
  13.  unsigned long data;   
  14.  
  15. };   
  16.  

各成員的含義如下:

      (1)next指針:指向下一個tasklet的指針。

      (2)state:定義了這個tasklet的當前狀態。這一個32位的無符號長整數,當前只使用了bit[1]和bit[0]兩個狀態位。其中,bit[1]=1表示這個tasklet當前正在某個CPU上被執行,它僅對SMP系統才有意義,其作用就是為了防止多個CPU同時執行一個tasklet的情形出現;bit[0]=1表示這個tasklet已經被調度去等待執行了。對這兩個狀態位的宏定義如下所示(interrupt.h):  

  1.  
  2.  
  3. view sourceprint?1 enum   
  4.  
  5. {   
  6.  
  7. TASKLET_STATE_SCHED, /* Tasklet is scheduled for execution */   
  8.  
  9. TASKLET_STATE_RUN /* Tasklet is running (SMP only) */   
  10.  
  11.  };   
  12.  

      (3)原子計數count:對這個tasklet的引用計數值。NOTE!只有當count等于0時,tasklet代碼段才能執行,也即此時tasklet是被使能的;如果count非零,則這個tasklet是被禁止的。任何想要執行一個tasklet代碼段的人都首先必須先檢查其count成員是否為0。

      (4)函數指針func:指向以函數形式表現的可執行tasklet代碼段。

     (5)data:函數func的參數。這是一個32位的無符號整數,其具體含義可供func函數自行解釋,比如將其解釋成一個指向某個用戶自定義數據結構的地址值。

Linux在interrupt.h頭文件中又定義了兩個用來定義 tasklet_struct 結構變量的輔助宏:

 

  1. view sourceprint?1 #define DECLARE_TASKLET(name, func, data)   
  2.  
  3.  struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }   
  4.  
  5.    
  6.  
  7. #define DECLARE_TASKLET_DISABLED(name, func, data)   
  8.  
  9.  struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data }   
  10.  

       顯然,從上述源代碼可以看出,用 DECLARE_TASKLET 宏定義的tasklet在初始化時是被使能的(enabled),因為其count成員為0。而用 DECLARE_TASKLET_DISABLED 宏定義的tasklet在初始時是被禁止的(disabled),因為其count等于1。

在這里,tasklet狀態指兩個方面:1. state成員所表示的運行狀態;2. count成員決定的使能/禁止狀態。

#p#

        (1)改變一個tasklet的運行狀態 state 成員中的bit[0]表示一個tasklet是否已被調度去等待執行,bit[1]表示一個tasklet是否正在某個CPU上執行。對于 state 變量中某位的改變必須是一個原子操作,因此可以用定義在include/asm/bitops.h頭文件中的位操作來進行。

        由于bit[1]這一位(即TASKLET_STATE_RUN)僅僅對于SMP系統才有意義,因此Linux在Interrupt.h頭文件中顯示地定義了對TASKLET_STATE_RUN位的操作。如下所示:

 

 

 

 

  1. view sourceprint?1 #ifdef CONFIG_SMP   
  2.  
  3. #define tasklet_trylock(t) (!test_and_set_bit(TASKLET_STATE_RUN, &(t)->state))   
  4.  
  5. #define tasklet_unlock_wait(t) while (test_bit(TASKLET_STATE_RUN, &(t)->state)) { /* NOTHING */ }   
  6.  
  7. #define tasklet_unlock(t) clear_bit(TASKLET_STATE_RUN, &(t)->state)   
  8.  
  9. #else   
  10.  
  11. #define tasklet_trylock(t) 1   
  12.  
  13. #define tasklet_unlock_wait(t) do { } while (0)   
  14.  
  15. #define tasklet_unlock(t) do { } while (0)   
  16.  
  17.  #endif   
  18.  

        顯然,在SMP系統同,tasklet_trylock() 宏將把一個 tasklet_struct 結構變量中的state成員中的bit[1]位設置成1,同時還返回bit[1]位的非。因此,如果bit[1]位原有值為1(表示另外一個CPU正在執行這個tasklet代碼),那么tasklet_trylock()宏將返回值0,也就表示上鎖不成功。如果bit[1]位的原有值為0,那么tasklet_trylock()宏將返回值1,表示加鎖成功。而在單CPU系統中,tasklet_trylock()宏總是返回為1。

        任何想要執行某個tasklet代碼的程序都必須首先調用宏 tasklet_trylock() 來試圖對這個tasklet進行上鎖(即設置TASKLET_STATE_RUN位),且只能在上鎖成功的情況下才能執行這個tasklet。建議!即使你的程序只在單 CPU 系統上運行,你也要在執行tasklet之前調用tasklet_trylock()宏,以便使你的代碼獲得良好可移植性。

        在SMP系統中,tasklet_unlock_wait() 宏將一直不停地測試 TASKLET_STATE_RUN 位的值,直到該位的值變為0(即一直等待到解鎖),假如:CPU0正在執行tasklet A的代碼,在此期間,CPU1也想執行tasklet A的代碼,但CPU1發現tasklet A 的 TASKLET_STATE_RUN 位為1,于是它就可以通過 tasklet_unlock_wait() 宏等待tasklet A被解鎖(也即TASKLET_STATE_RUN位被清零)。在單CPU系統中,這是一個空操作。

        宏 tasklet_unlock() 用來對一個 tasklet 進行解鎖操作,也即將TASKLET_STATE_RUN位清零。在單CPU系統中,這是一個空操作。

(2)使能/禁止一個tasklet

       使能與禁止操作往往總是成對地被調用的,tasklet_disable() 函數如下

 

 

 

 

  1. (interrupt.h):  
  2. view sourceprint?01 static inline void tasklet_disable(struct tasklet_struct *t)   
  3.  
  4. {   
  5.  
  6. tasklet_disable_nosync(t);   
  7.  
  8. tasklet_unlock_wait(t);   
  9. }   
  10.  
  11. // 函數tasklet_disable_nosync()也是一個靜態inline函數,它簡單地通過原子操作將count成員變量的值減1。如下所示(interrupt.h):   
  12.  
  13. static inline void tasklet_disable_nosync(struct tasklet_struct *t)   
  14.  
  15. {   
  16.  
  17. atomic_inc(&t->count);   
  18.  
  19. }   
  20.  
  21. // 函數tasklet_enable()用于使能一個tasklet,如下所示(interrupt.h):   
  22.  
  23. static inline void tasklet_enable(struct tasklet_struct *t)   
  24.  
  25. {   
  26.  
  27. atomic_dec(&t->count);   
  28.  
  29. }   
  30.  
  31. // 函數tasklet_init()用來初始化一個指定的tasklet描述符,其源碼如下所示(kernel/softirq.c):   
  32.  
  33. void tasklet_init(struct tasklet_struct *t,   
  34.  
  35. void (*func)(unsigned long),   
  36.  
  37. unsigned long data)   
  38.  
  39. {   
  40.  
  41. t->funcfunc = func;   
  42.  
  43. t->datadata = data;   
  44.  
  45. t->state = 0;   
  46.  
  47. atomic_set(&t->count, 0);   
  48.  
  49. }   
  50.  
  51. // 函數tasklet_kill()用來將一個已經被調度了的tasklet殺死,即將其恢復到未調度的狀態。其源碼如下所示(kernel/softirq.c):   
  52.  
  53. void tasklet_kill(struct tasklet_struct *t)   
  54.  
  55. {   
  56.  
  57. if (in_interrupt())   
  58.  
  59. printk("Attempt to kill tasklet from interruptn");   
  60.  
  61. while (test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) {   
  62.  
  63. current->state = TASK_RUNNING;   
  64.  
  65. do {   
  66.  
  67. current->policy |= SCHED_YIELD;   
  68.  
  69. schedule();   
  70.  
  71. } while (test_bit(TASKLET_STATE_SCHED, &t->state));   
  72.  
  73. }   
  74.  
  75. tasklet_unlock_wait(t);   
  76.  
  77.  clear_bit(TASKLET_STATE_SCHED, &t->state);   
  78.  
  79. }   
  80.  
  81.  // 多個tasklet可以通過tasklet描述符中的next成員指針鏈接成一個單向對列。為此,Linux專門在頭文件include/linux/interrupt.h中定義了數據結構tasklet_head來描述一個tasklet對列的頭部指針。如下所示:   
  82.  
  83.  struct tasklet_head   
  84.  
  85.  {   
  86.  
  87.  struct tasklet_struct *list;   
  88.  
  89.  } __attribute__ ((__aligned__(SMP_CACHE_BYTES)));   
  90.  

        盡管 tasklet 機制是特定于軟中斷向量HI_SOFTIRQ和TASKLET_SOFTIRQ的一種實現,但是tasklet機制仍然屬于softirq機制的整體框架范圍內的,因此,它的設計與實現仍然必須堅持“誰觸發,誰執行”的思想。為此,Linux為系統中的每一個CPU都定義了一個 tasklet 對列頭部,來表示應該有各個CPU負責執行的tasklet對列。如下所示(kernel/softirq.c):

       

  1. struct tasklet_head tasklet_vec[NR_CPUS] __cacheline_aligned;struct tasklet_head tasklet_hi_vec[NR_CPUS] __cacheline_aligned;  

#p#

        其中,tasklet_vec[]數組用于軟中斷向量TASKLET_SOFTIRQ,而tasklet_hi_vec[]數組則用于軟中斷向量HI_SOFTIRQ。也即,如果CPUi(0≤i≤NR_CPUS-1)觸發了軟中斷向量TASKLET_SOFTIRQ,那么對列tasklet_vec[i]中的每一個tasklet都將在CPUi服務于軟中斷向量TASKLET_SOFTIRQ時被CPUi所執行。同樣地,如果CPUi(0≤i≤NR_CPUS-1)觸發了軟中斷向量HI_SOFTIRQ,那么隊列tasklet_vec[i]中的每一個tasklet都將CPUi在對軟中斷向量HI_SOFTIRQ進行服務時被CPUi所執行。

        隊列tasklet_vec[I]和tasklet_hi_vec[I]中的各個tasklet是怎樣被所CPUi所執行的呢?其關鍵就是軟中斷向量TASKLET_SOFTIRQ和HI_SOFTIRQ的軟中斷服務程序——tasklet_action()函數和tasklet_hi_action()函數。下面我們就來分析這兩個函數。

       Linux為軟中斷向量TASKLET_SOFTIRQ和HI_SOFTIRQ實現了專用的觸發函數和軟中斷服務函數。其中,tasklet_schedule() 函數和 tasklet_hi_schedule() 函數分別用來在當前CPU上觸發軟中斷向量TASKLET_SOFTIRQ 和 HI_SOFTIRQ,并把指定的tasklet 加入當前CPU所對應的 tasklet 隊列中去等待執行。而tasklet_action() 函數和 tasklet_hi_action() 函數則分別是軟中斷向量 TASKLET_SOFTIRQ 和 HI_SOFTIRQ 的軟中斷服務函數。在初始化函數 softirq_init() 中,這兩個軟中斷向量對應的描述符softirq_vec[0]和softirq_vec[3]中的action函數指針就被分別初始化成指向函數 tasklet_hi_action() 和函數 tasklet_action()。

    (1)軟中斷向量TASKLET_SOFTIRQ的觸發函數 tasklet_schedule()

       該函數實現在include/linux/interrupt.h頭文件中,是一個 inline 函數。其源碼如下所示:
 

 

 

 

  1. view sourceprint?01 static inline void tasklet_schedule(struct tasklet_struct *t)   
  2. {   
  3. if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) {   
  4. int cpu = smp_processor_id();   
  5. unsigned long flags;   
  6. local_irq_save(flags);   
  7. t->next = tasklet_vec[cpu].list;   
  8. tasklet_vec[cpu].list = t;   
  9. __cpu_raise_softirq(cpu, TASKLET_SOFTIRQ);   
  10. local_irq_restore(flags);   
  11. }   
  12. }   

       該函數的參數t指向要在當前CPU上被執行的 tasklet。對該函數的NOTE如下:

       ①調用test_and_set_bit()函數將待調度的 tasklet 的state成員變量的bit[0]位(也即TASKLET_STATE_SCHED位)設置為1,該函數同時還返回TASKLET_STATE_SCHED位的原有值。因此如果bit[0]為的原有值已經為1,那就說明這個tasklet已經被調度到另一個CPU上去等待執行了。由于一個tasklet在某一個時刻只能由一個CPU來執行,因此tasklet_schedule()函數什么也不做就直接返回了。否則,就繼續下面的調度操作。

       ②首先,調用 local_irq_save() 函數來關閉當前CPU的中斷,以保證下面的步驟在當前CPU上原子地被執行。

       ③然后,將待調度的 tasklet 添加到當前CPU對應的 tasklet 隊列的首部。

       ④接著,調用 __cpu_raise_softirq() 函數在當前CPU上觸發軟中斷請求TASKLET_SOFTIRQ。

       ⑤***,調用local_irq_restore() 函數來開當前CPU的中斷。

(2)軟中斷向量TASKLET_SOFTIRQ的服務程序tasklet_action()

       函數tasklet_action()是tasklet機制與軟中斷向量TASKLET_SOFTIRQ的聯系紐帶。正是該函數

  1. view sourceprint?01 static inline void tasklet_hi_schedule(struct tasklet_struct *t)   
  2.  
  3. {   
  4.  
  5. if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) {   
  6.  
  7. int cpu = smp_processor_id();   
  8.  
  9. unsigned long flags;   
  10.  
  11. local_irq_save(flags);   
  12.  
  13. t->next = tasklet_hi_vec[cpu].list;   
  14.  
  15. tasklet_hi_vec[cpu].list = t;   
  16.  
  17. __cpu_raise_softirq(cpu, HI_SOFTIRQ);   
  18.  
  19. local_irq_restore(flags);   
  20.  
  21. }  
  22.    
  23. }   

將當前CPU的tasklet隊列中的各個tasklet放到當前CPU上來執行的。該函數實現在kernel/softirq.c文件中,其源代碼如下:

  1. view sourceprint?01 static void tasklet_action(struct softirq_action *a)   
  2.  
  3.  {   
  4.  
  5.  int cpu = smp_processor_id();   
  6.  
  7.  struct tasklet_struct *list;   
  8.  
  9.  local_irq_disable();   
  10.  
  11.  list = tasklet_vec[cpu].list;   
  12.  
  13.  tasklet_vec[cpu].list = NULL;   
  14.  
  15.  local_irq_enable();   
  16.  
  17.  while (list != NULL) {   
  18.  
  19.  struct tasklet_struct *t = list;   
  20.  
  21.  listlist = list->next;   
  22.  
  23.  if (tasklet_trylock(t)) {   
  24.  
  25.  if (atomic_read(&t->count) == 0) {   
  26.  
  27.  clear_bit(TASKLET_STATE_SCHED, &t->state);   
  28.  
  29.  t->func(t->data);   
  30.  
  31.  /*   
  32.  
  33.  * talklet_trylock() uses test_and_set_bit that imply   
  34.  
  35.  * an mb when it returns zero, thus we need the explicit   
  36.  
  37.  * mb only here: while closing the critical section.   
  38.  
  39.  */   
  40.  
  41.  #ifdef CONFIG_SMP   
  42.  
  43.  smp_mb__before_clear_bit();   
  44.  
  45.  #endif   
  46.  
  47.  tasklet_unlock(t);   
  48.  
  49.  continue;   
  50.  
  51.  }   
  52.  
  53.  tasklet_unlock(t);   
  54.  
  55.  }   
  56.  
  57.  local_irq_disable();   
  58.  
  59.  t->next = tasklet_vec[cpu].list;   
  60.  
  61.  tasklet_vec[cpu].list = t;   
  62.  
  63.  __cpu_raise_softirq(cpu, TASKLET_SOFTIRQ);   
  64.  
  65.  local_irq_enable();   
  66.  
  67.  }   
  68.  
  69.  }   

注釋如下:

       ①首先,在當前CPU關中斷的情況下,“原子”地讀取當前CPU的tasklet隊列頭部指針,將其保存到局部變量list指針中,然后將當前CPU的tasklet隊列頭部指針設置為NULL,以表示理論上當前CPU將不再有tasklet需要執行(但***的實際結果卻并不一定如此,下面將會看到)。

       ②然后,用一個while{}循環來遍歷由list所指向的tasklet隊列,隊列中的各個元素就是將在當前CPU上執行的tasklet。循環體的執行步驟如下:

        用指針t來表示當前隊列元素,即當前需要執行的tasklet。
        更新list指針為list->next,使它指向下一個要執行的tasklet。
        用tasklet_trylock()宏試圖對當前要執行的tasklet(由指針t所指向)進行加鎖,如果加鎖成功(當前沒有任何其他CPU正在執行這個tasklet),則用原子讀函數atomic_read()進一步判斷count成員的值。如果count為0,說明這個tasklet是允許執行的,于是:a先清除TASKLET_STATE_SCHED位;然后,調用這個tasklet的可執行函數func;執行barrier()操作;調用宏tasklet_unlock()來清除TASKLET_STATE_RUN位。***,執行continue語句跳過下面的步驟,回到while循環繼續遍歷隊列中的下一個元素。如果count不為0,說明這個tasklet是禁止運行的,于是調用tasklet_unlock()清除前面用tasklet_trylock()設置的TASKLET_STATE_RUN位。
        如果tasklet_trylock()加鎖不成功,或者因為當前tasklet的count值非0而不允許執行時,我們必須將這個tasklet重新放回到當前CPU的tasklet隊列中,以留待這個CPU下次服務軟中斷向量TASKLET_SOFTIRQ時再執行。為此進行這樣幾步操作:先關CPU中斷,以保證下面操作的原子性。把這個tasklet重新放回到當前CPU的tasklet隊列的首部;調用__cpu_raise_softirq()函數在當前CPU上再觸發一次軟中斷請求TASKLET_SOFTIRQ;開中斷。 ***,回到while循環繼續遍歷隊列。 
 

#p#

       (3)軟中斷向量HI_SOFTIRQ的觸發函數tasklet_hi_schedule()

該函數與tasklet_schedule()幾乎相同,其源碼如下(include/linux/interrupt.h):
 

 

 

  1.  
  2. view sourceprint?01 static inline void tasklet_hi_schedule(struct tasklet_struct *t)   
  3.  
  4.  {   
  5.  
  6.  if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) {   
  7.  
  8.  int cpu = smp_processor_id();   
  9.  
  10.  unsigned long flags;   
  11.  
  12.  local_irq_save(flags);   
  13.  
  14.  t->next = tasklet_hi_vec[cpu].list;   
  15.  
  16.  tasklet_hi_vec[cpu].list = t;   
  17.  
  18.  __cpu_raise_softirq(cpu, HI_SOFTIRQ);   
  19.  
  20.  local_irq_restore(flags);   
  21.  
  22.  }   
  23.  
  24.  }   

     (4)軟中斷向量HI_SOFTIRQ的服務函數tasklet_hi_action()

該函數與tasklet_action()函數幾乎相同,其源碼如下(kernel/softirq.c):


 

 

 

  1. view sourceprint?01 static void tasklet_hi_action(struct softirq_action *a)   
  2.  
  3.  {   
  4.  
  5.  int cpu = smp_processor_id();   
  6.  
  7.  struct tasklet_struct *list;   
  8.  
  9.  local_irq_disable();   
  10.  
  11.  list = tasklet_hi_vec[cpu].list;   
  12.  
  13.  tasklet_hi_vec[cpu].list = NULL;   
  14.  
  15.  local_irq_enable();   
  16.    
  17.  while (list != NULL)  
  18.  
  19.  {   
  20.  
  21.  struct tasklet_struct *t = list;   
  22.  
  23.  listlist = list->next;   
  24.  
  25.  if (tasklet_trylock(t)) {   
  26.  
  27.  if (atomic_read(&t->count) == 0) {   
  28.  
  29.  clear_bit(TASKLET_STATE_SCHED, &t->state);   
  30.  
  31.  t->func(t->data);   
  32.  
  33.  tasklet_unlock(t);   
  34.  
  35.  continue;   
  36.  
  37.  }   
  38.  
  39.  tasklet_unlock(t);   
  40.  
  41.  }   
  42.  
  43.  local_irq_disable();   
  44.  
  45.  t->next = tasklet_hi_vec[cpu].list;   
  46.  
  47.  tasklet_hi_vec[cpu].list = t;   
  48.  
  49.  __cpu_raise_softirq(cpu, HI_SOFTIRQ);   
  50.  
  51.  local_irq_enable();   
  52.  
  53.  }   
  54.  
  55.  }   

        Bottom Half 機制在新的softirq機制中被保留下來,并作為softirq框架的一部分。其實現也似乎更為復雜些,因為它是通過 tasklet 機制這個中介橋梁來納入softirq框架中的。實際上,軟中斷向量 HI_SOFTIRQ 是內核專用于執行BH函數的。原有的32個BH函數指針被保留,定義在kernel/softirq.c文件中:static void (*bh_base[32])(void);


       但是,每個BH函數都對應有一個tasklet,并由tasklet的可執行函數func來負責調用相應的bh函數(func函數的參數指定調用哪一個BH函數)。與32個BH函數指針相對應的tasklet的定義如下所示(kernel/softirq.c):struct tasklet_struct bh_task_vec[32];


       上述tasklet數組使系統全局的,它對所有的CPU均可見。由于在某一個時刻只能有一個CPU在執行BH函數,因此定義一個全局的自旋鎖來保護BH函數,如下所示(kernel/softirq.c):spinlock_t global_bh_lock = SPIN_LOCK_UNLOCKED;


       在softirq機制的初始化函數softirq_init()中將bh_task_vec[32]數組中的每一個tasklet中的func函數指針都設置為指向同一個函數bh_action,而data成員(也即func函數的調用參數)則被設置成該tasklet在數組中的索引值。因此,bh_action()函數將負責相應地調用參數所指定的bh函數。該函數是連接 tasklet機制與Bottom Half機制的關鍵所在。

該函數的源碼如下(kernel/softirq.c):
 

  1. view sourceprint?1 void __init softirq_init()   
  2.  
  3. 2 {   
  4.  
  5. 3 ……   
  6.  
  7. 4 for (i=0; i<32; i++)   
  8.  
  9. 5 tasklet_init(bh_task_vec+i, bh_action, i);   
  10.  
  11. 6 ……   
  12.  
  13. 7 }   
  14.  
  15.  
  16.  
  17. view sourceprint?01 static void bh_action(unsigned long nr)   
  18.  
  19.  {   
  20.  
  21.  int cpu = smp_processor_id();   
  22.  
  23.  if (!spin_trylock(&global_bh_lock))   
  24.  
  25.  goto resched;   
  26.  
  27.  if (!hardirq_trylock(cpu))   
  28.  
  29.  goto resched_unlock;   
  30.  
  31.  if (bh_base[nr])   
  32.  
  33.  bh_base[nr]();   
  34.  
  35.  hardirq_endlock(cpu);   
  36.  
  37.  spin_unlock(&global_bh_lock);   
  38.  
  39.  return;   
  40.  
  41.  resched_unlock:   
  42.  
  43.  spin_unlock(&global_bh_lock);   
  44.  
  45.  resched:   
  46.  
  47.  mark_bh(nr);   
  48.  
  49.  }   

對該函數的注釋如下:

         ①首先,調用spin_trylock()函數試圖對自旋鎖global_bh_lock進行加鎖,同時該函數還將返回自旋鎖global_bh_lock的原有值的非。因此,如果global_bh_lock已被某個CPU上鎖而為非0值(那個CPU肯定在執行某個BH函數),那么spin_trylock()將返回為0表示上鎖失敗,在這種情況下,當前CPU是不能執行BH函數的,因為另一個CPU正在執行BH函數,于是執行goto語句跳轉到resched程序段,以便在當前CPU上再一次調度該BH函數。

        ②調用hardirq_trylock()函數鎖定當前CPU,確保當前CPU不是處于硬件中斷請求服務中,如果鎖定失敗,跳轉到resched_unlock程序段,以便先對global_bh_lock解鎖,在重新調度一次該BH函數。

       ③此時,我們已經可以放心地在當前CPU上執行BH函數了。當然,對應的BH函數指針bh_base[nr]必須有效才行。

       ④從BH函數返回后,先調用hardirq_endlock()函數(實際上它什么也不干,調用它只是為了保此加、解鎖的成對關系),然后解除自旋鎖global_bh_lock,***函數就可以返回了。

       ⑤resched_unlock程序段:先解除自旋鎖global_bh_lock,然后執行reched程序段。

       ⑥resched程序段:當某個CPU正在執行BH函數時,當前CPU就不能通過bh_action()函數來調用執行任何BH函數,所以就通過調用mark_bh()函數在當前CPU上再重新調度一次,以便將這個BH函數留待下次軟中斷服務時執行。

(1)init_bh()函數

        該函數用來在bh_base[]數組登記一個指定的bh函數,如下所示(kernel/softirq.c):

  1. view sourceprint?1 void init_bh(int nr, void (*routine)(void))   
  2.  
  3.  {   
  4.  
  5.  bh_base[nr] = routine;   
  6.  
  7.  mb();   
  8.  
  9.  }   

(2)remove_bh()函數

該函數用來在bh_base[]數組中注銷指定的函數指針,同時將相對應的tasklet殺掉。

如下所示(kernel/softirq.c):

  1. view sourceprint?1 void remove_bh(int nr)   
  2.  
  3.  {   
  4.  
  5.  tasklet_kill(bh_task_vec+nr);   
  6.  
  7.  bh_base[nr] = NULL;   
  8.  
  9.  }   

(3)mark_bh()函數

       該函數用來向當前CPU標記由一個BH函數等待去執行。它實際上通過調用tasklet_hi_schedule()函數將相應的tasklet加入到當前CPU的tasklet隊列tasklet_hi_vec[cpu]中,然后觸發軟中斷請求HI_SOFTIRQ,如下所示(include/linux/interrupt.h):
 

  1. view sourceprint?1 static inline void mark_bh(int nr)   
  2.  
  3. {   
  4.  
  5. tasklet_hi_schedule(bh_task_vec+nr);   
  6.  
  7. }   

       在32個BH函數指針中,大多數已經固定用于一些常見的外設,比如:第0個BH函數就固定地用于時鐘中斷。Linux在頭文件include/linux/interrupt.h中定義了這些已經被使用的BH函數所引,如下所示:

  1. view sourceprint?01 enum  
  2.  
  3. {   
  4.  
  5.  TIMER_BH = 0,   
  6.  
  7.  TQUEUE_BH,   
  8.  
  9.  DIGI_BH,   
  10.  
  11.  SERIAL_BH,   
  12.  
  13.  RISCOM8_BH,   
  14.  
  15.  SPECIALIX_BH,   
  16.  
  17.  AURORA_BH,   
  18.  
  19.  ESP_BH,   
  20.  
  21.  SCSI_BH,   
  22.  
  23.  IMMEDIATE_BH,   
  24.  
  25.  CYCLADES_BH,   
  26.  
  27.  CM206_BH,   
  28.  
  29.  JS_BH,   
  30.  
  31.  MACSERIAL_BH,   
  32.  
  33.  ISICOM_BH   
  34.  
  35.  };   

         從以上的例子可以看出,所謂小任務機制就是為下半部分函數提供的一種執行機制,也就是說推遲處理的事情由tasklet_handler實現。經過小任務封裝以后再交給內核去處理。以上就是tasklet機制在linux中的實現  ,使得tasklet機制與一般意義上的軟中斷有所不同

【編輯推薦】

  1. 全面認識Flex事件機制
  2. 網站運維之道 監控與報警機制
  3. 三種Flex數據訪問機制
  4. 提高Linux操作系統性能
  5. 熱門Linux桌面環境挨個看
  6. Linux操作系統中運行ASP.NET 4

 

責任編輯:zhaolei
相關推薦

2011-04-22 17:29:37

Linux網卡

2013-10-31 16:29:10

Linux內核

2011-01-10 18:21:38

linux編寫程序

2010-01-07 13:27:22

Linux驅動程序

2021-11-29 07:55:45

Linux GPIO Linux 系統

2009-12-07 09:39:04

Linux設備驅動硬件通信

2009-07-06 18:17:46

JDBC驅動程序

2021-12-06 07:47:36

Linux 驅動程序Linux 系統

2009-10-23 10:25:27

驅動程序技巧

2022-05-13 09:14:47

NVidia開源Linux

2009-11-30 14:51:00

Linux設置無線網卡

2018-11-26 08:45:29

Linux驅動程序命令

2011-08-16 16:32:13

Linux驅動程序

2022-05-23 13:17:32

Linux開源NVIDIA

2017-10-24 17:03:48

Linux驅動程序編譯

2017-03-03 08:40:32

2009-08-12 18:20:39

C#事件驅動程序

2018-11-19 10:15:26

Windows 10WiFi驅動程序

2021-11-16 06:55:36

Linux字符設備

2009-07-20 18:01:38

Oracle JDBC
點贊
收藏

51CTO技術棧公眾號

国产亚洲欧美日韩日本| 亚洲日本视频| 宅男在线国产精品| 中文字幕在线中文| 欧美视频久久久| 日日夜夜免费精品视频| 久久久黄色av| aa片在线观看视频在线播放| aaaa欧美| 亚洲国产三级在线| 日韩精品一区二区三区四区五区| 国产精品人人爽| 99在线|亚洲一区二区| 一本一本久久a久久精品综合小说| 三日本三级少妇三级99| 一二三四视频在线中文| 中文字幕亚洲综合久久菠萝蜜| 亚洲精品欧美一区二区三区| 亚洲欧美偷拍视频| 欧美日韩精选| 亚洲精品永久免费| 91传媒理伦片在线观看| 久久精品国产精品亚洲毛片| 精品久久香蕉国产线看观看亚洲| 好吊色这里只有精品| 日本高清中文字幕二区在线| 国产一区二区日韩精品| 国产成人av网| 日本少妇xxxx动漫| 91精品亚洲| 伊人久久久久久久久久久久久| 精品人妻伦一二三区久| 精品91福利视频| 在线视频综合导航| 欧美牲交a欧美牲交| 2024最新电影免费在线观看 | 日韩av加勒比| 视频在线日韩| 色综合欧美在线视频区| 成人免费播放器| 在线xxxx| 亚洲三级在线播放| 亚洲国产精品123| 免费一级在线观看播放网址| 波多野结衣中文字幕一区 | 91精品国产乱码久久久| 久久裸体视频| 欧美在线观看网站| 黄色片免费观看视频| 亚洲欧洲视频| 国内精品久久久久久| 久久国产精品二区| 忘忧草精品久久久久久久高清| 一区二区三区视频在线| 四季av中文字幕| 成人羞羞网站入口免费| 国产一区av在线| 日本一级免费视频| 国产亚洲一卡2卡3卡4卡新区| 日韩毛片在线观看| 亚洲精品理论片| 亚洲欧洲免费| 亚洲香蕉伊综合在人在线视看| 波多野吉衣中文字幕| 超碰在线一区| 亚洲娇小xxxx欧美娇小| 在线精品一区二区三区| 亚洲最大在线| 国产一区二区三区在线免费观看| 亚洲精品国产一区黑色丝袜| 波多野结衣在线播放一区| 一本一道久久a久久精品逆3p| 九九热免费在线| 99久久九九| 久久综合伊人77777蜜臀| 欧产日产国产v| 在线成人h网| 日本精品va在线观看| 久久精品99北条麻妃| 另类专区欧美蜜桃臀第一页| 91在线网站视频| 三级在线观看网站| 国产欧美一区二区三区鸳鸯浴| 亚洲精品无人区| gogogogo高清视频在线| 一个色综合网站| 337p粉嫩大胆噜噜噜鲁| 电影亚洲精品噜噜在线观看| 欧美日韩国产小视频| 九九热视频免费| 免费成人三级| 色香阁99久久精品久久久| 国产盗摄一区二区三区在线| 国产午夜精品一区二区三区欧美| 国产精品视频区| 成人激情四射网| 久久免费精品国产久精品久久久久| 婷婷精品国产一区二区三区日韩| a级在线观看| 欧美日韩亚洲一区二区| 久国产精品视频| 麻豆一区二区| 久久国产一区二区三区| 五月婷婷亚洲综合| 国产精品亚洲а∨天堂免在线| 久久99久久精品国产| 男人天堂久久久| 岛国视频午夜一区免费在线观看| 国产小视频精品| 久本草在线中文字幕亚洲| 中文字幕一精品亚洲无线一区| 国产精品白浆一区二小说| 理论电影国产精品| 免费在线成人av| 日本孕妇大胆孕交无码| 欧美亚洲图片小说| 黄色录像a级片| 一区二区蜜桃| 国产精品视频一区国模私拍| 十八禁一区二区三区| 亚洲欧美综合网| 动漫av免费观看| 国产精品15p| 米奇精品一区二区三区在线观看| 波多野结衣家庭主妇| eeuss国产一区二区三区| 三年中文高清在线观看第6集| 欧美大片免费| 亚洲精品国偷自产在线99热| 久久国产免费观看| 激情综合一区二区三区| 亚洲高清资源综合久久精品| 日韩三级影视| 国产视频久久网| 国产精品免费av一区二区| 国产精品影视网| 一级一片免费播放| 成人交换视频| 国产亚洲人成a一在线v站| 男人日女人网站| av亚洲产国偷v产偷v自拍| 六月婷婷激情综合| 免费看日产一区二区三区| 久久精品国产欧美激情| 天天爱天天做天天爽| 久久久久一区二区三区四区| 国产最新免费视频| 色狠狠久久av综合| 51色欧美片视频在线观看| 日本美女一级视频| 性欧美疯狂xxxxbbbb| 中文字幕天堂av| 在线 亚洲欧美在线综合一区| 国产精品v欧美精品v日韩| 日本天码aⅴ片在线电影网站| 91精品国产综合久久蜜臀| 免费看特级毛片| 国产精品一品二品| 欧美黑人在线观看| 麻豆精品99| 国产精品九九九| 一级毛片视频在线| 67194成人在线观看| 无码人妻精品一区二区三区夜夜嗨| 加勒比av一区二区| 国产一二三四区在线观看| 日韩在线精品强乱中文字幕| 欧美精品激情视频| 四虎成人免费在线| 欧美综合在线视频| 熟女少妇a性色生活片毛片| 精久久久久久久久久久| 9191国产视频| 欧美国产不卡| 国产成人精品电影久久久| 97超碰人人在线| 欧美一区二区三区免费视频| 日韩成人一区二区三区| 久久尤物电影视频在线观看| 黄色在线视频网| 一区二区三区毛片免费| 国产一区二区三区高清视频| 免费观看亚洲| 久久精品免费播放| 神马午夜精品95| 欧美视频在线观看一区二区| 日韩成人短视频| 99久久er热在这里只有精品15 | 亚洲国产天堂久久国产91 | 亚洲色图100p| 国产福利精品一区| 国产成人无码一二三区视频| 五月激情久久久| 精品国产一区二区三区麻豆免费观看完整版 | 欧美国产中文字幕| 欧美日韩在线精品一区二区三区激情综 | 亚洲18在线| 97视频在线看| 欧美被日视频| 精品爽片免费看久久| 国产一区二区在线不卡| 精品色蜜蜜精品视频在线观看| 蜜桃av免费观看| 99精品一区二区三区| 亚洲视频一二三四| 午夜亚洲精品| 一本大道东京热无码aⅴ| 国产剧情在线观看一区| julia一区二区中文久久94| 亚洲四虎影院| 5566日本婷婷色中文字幕97| а√天堂在线官网| 永久555www成人免费| 色屁屁草草影院ccyycom| 欧美一三区三区四区免费在线看 | 免费视频久久| 欧美做暖暖视频| 欧美3p视频| 日韩成人av网站| 国产精品流白浆在线观看| 91青草视频久久| 日韩一级二级| 欧美一级bbbbb性bbbb喷潮片| 性欧美猛交videos| 日韩性生活视频| 国产资源在线看| 精品伊人久久97| 天天综合天天色| 精品国产一二三区| 精品久久人妻av中文字幕| 在线不卡一区二区| 欧美在线视频精品| 91久久人澡人人添人人爽欧美| 国产黄色片免费看| 亚洲成av人片www| 国产亚洲第一页| 一区二区三区丝袜| 日韩在线观看视频一区二区| 亚洲欧美综合色| 色欲一区二区三区精品a片| 国产精品乱人伦一区二区| 91精品国自产在线| 国产清纯白嫩初高生在线观看91 | 香蕉视频成人在线| 亚洲高清久久网| 色婷婷综合视频| 日韩电影中文字幕在线| 污视频在线免费观看| 日韩av一卡二卡| 少妇性bbb搡bbb爽爽爽欧美| 亚洲精品理论电影| 欧美扣逼视频| 国产香蕉97碰碰久久人人| 国产乱视频在线观看| 中文字幕9999| 日本精品一区二区三区在线播放| 精品国产欧美一区二区三区成人| 老司机在线视频二区| 久久天天躁夜夜躁狠狠躁2022| 黄色网址视频在线观看| 美日韩在线视频| av资源中文在线| 欧美主播福利视频| 国产精品久久久久久吹潮| 91精品久久久久久久久不口人| 国产精久久一区二区| 99久久久久国产精品免费| 看全色黄大色大片免费久久久| 久久国产精品久久| av一区二区在线播放| 成年人免费观看的视频| 在线不卡欧美| 欧美少妇性生活视频| 老司机免费视频一区二区 | 人人妻人人爽人人澡人人精品| 色综合久久久久久久| 岳乳丰满一区二区三区| 日韩一区二区在线观看视频| 欧美视频在线观看一区二区三区| 亚洲美女自拍视频| 素人av在线| 久久久久久高潮国产精品视| 亚洲精品福利电影| 91精品久久久久久久| 国产精品1luya在线播放| 日本一区二区免费看| 91精品国产乱码久久久久久| www..com日韩| 七七婷婷婷婷精品国产| 国产欧美视频一区| 国产欧美日韩视频在线观看| 欧美交换国产一区内射| 色丁香久综合在线久综合在线观看| 国产又黄又爽视频| 日韩精品视频免费专区在线播放 | 亚洲嫩草精品久久| 亚洲精品午夜国产va久久成人| 欧美美女激情18p| 天堂а在线中文在线无限看推荐| 色妞欧美日韩在线| 超碰91在线观看| 成人免费高清完整版在线观看| 国产乱人伦精品一区| 一区二区视频在线播放| av成人天堂| 中文字幕无码毛片免费看| 久久精品视频一区二区三区| 亚洲欧美一区二区三区四区五区| 日本久久电影网| 六月丁香综合网| 久久在线免费视频| 欧美性理论片在线观看片免费| 国产精品久久九九| 天天久久综合| 午夜免费一区二区| 91视频精品在这里| 国产亚洲精品久久777777| 欧美精品乱人伦久久久久久| 国产视频二区在线观看| 孩xxxx性bbbb欧美| 中文字幕区一区二区三| 伊人久久大香线蕉精品| 日日嗨av一区二区三区四区| 喷水视频在线观看| 夜夜夜精品看看| 国产麻豆免费视频| 色琪琪综合男人的天堂aⅴ视频| 惠美惠精品网| 久久涩涩网站| 国产欧美69| 成人午夜精品无码区| 亚洲一区二区精品视频| 99热精品在线播放| 日韩一区视频在线| 欧美在线se| 亚洲7777| 麻豆精品蜜桃视频网站| 日本乱子伦xxxx| 色网站国产精品| 日韩偷拍自拍| 欧美亚洲日本黄色| 亚洲国产精品嫩草影院久久av| 每日在线观看av| 99热在这里有精品免费| 日韩成人在线免费视频| 亚洲国产成人精品电影| 182在线视频观看| 精品久久久三级| 亚洲综合日本| 天堂久久精品忘忧草| 91国产免费观看| 伊人免费在线| 91午夜理伦私人影院| 欧美韩日精品| 无码人妻精品一区二区三区99不卡| 亚洲国产综合在线| 手机在线观看毛片| 欧美一乱一性一交一视频| 蜜桃精品wwwmitaows| 黄色成人免费看| 中文字幕五月欧美| 国产视频手机在线观看| 欧美激情视频一区| 久久资源综合| 爆乳熟妇一区二区三区霸乳| 国产精品视频麻豆| 99久久精品日本一区二区免费| 欧美俄罗斯乱妇| 全国精品免费看| 五月婷婷激情久久| 亚洲欧美偷拍卡通变态| 亚洲国产精品久久久久爰性色 | 亚洲人成电影网站色xx| 视频精品导航| 男女啪啪免费观看| 99国产精品一区| 日本一区二区三区久久| 久久大大胆人体| 免费观看成人www动漫视频| 天天爱天天操天天干| 亚洲你懂的在线视频| 亚洲av成人无码久久精品老人 | 国产色一区二区| 国产农村妇女毛片精品久久| 国语自产偷拍精品视频偷| 波多野结衣在线观看一区二区三区| 欧美高清精品一区二区| 精品久久久中文| 精品孕妇一区二区三区| 国产视频一区二区三区四区| 日本va欧美va精品发布| 麻豆changesxxx国产| 亚洲日韩中文字幕在线播放| 精品国产乱码一区二区三区 | 欧美成人一区二区三区| 国产高清不卡| 中文字幕色呦呦| 国产色婷婷亚洲99精品小说| 国产极品999| 国产精品国产三级国产aⅴ9色|