Linux设备驱动下的tasklet


在设备驱动的中断处理中经常会用到tasklet,在前面稍微看了下linux的软中断后,tasklet就很容易理解了。Tasklet也要用到软中断,而tasklet的用法和定时器的用法很相似。

同样的在main.c中,

start_kernel-->softirq_init

先给出tasklet的结构体定义:

  1. struct tasklet_struct  
  2. {  
  3.     struct tasklet_struct *next;  
  4.     unsigned long state;  
  5.     atomic_t count;  
  6.     void (*func)(unsigned long);  
  7.     unsigned long data;  
  8. };  
  9.   
  10. struct tasklet_head  
  11. {  
  12.     struct tasklet_struct *head;  
  13.     struct tasklet_struct **tail;  
  14. };  
  15.   
  16. static DEFINE_PER_CPU(struct tasklet_head, tasklet_vec);  
  17. static DEFINE_PER_CPU(struct tasklet_head, tasklet_hi_vec);  
  18.   
  19. void __init softirq_init(void)  
  20. {  
  21.     int cpu;  
  22.   
  23.     for_each_possible_cpu(cpu) {  
  24.         int i;  
  25.   
  26.         per_cpu(tasklet_vec, cpu).tail =  
  27.             &per_cpu(tasklet_vec, cpu).head;  
  28.         per_cpu(tasklet_hi_vec, cpu).tail =  
  29.             &per_cpu(tasklet_hi_vec, cpu).head;  
  30.         for (i = 0; i < NR_SOFTIRQS; i++)  
  31.             INIT_LIST_HEAD(&per_cpu(softirq_work_list[i], cpu));  
  32.     }  
  33.   
  34.     register_hotcpu_notifier(&remote_softirq_cpu_notifier);  
  35.   
  36.     open_softirq(TASKLET_SOFTIRQ, tasklet_action);  
  37.     open_softirq(HI_SOFTIRQ, tasklet_hi_action);  
  38. }  

这样在softirq_init函数中,首先初始化tasklet_vec

然后注册tasklet软中断,www.bkjia.com 中断服务程序是tasklet_action

软中断的执行还是由ksoftirqd内核线程来处理。

下面看下tasklet的初始化:

  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的调度:

  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_struct加入这个tasklet_vec向量表中

这里调用raise_softirq_irqoff(TASKLET_SOFTIRQ);

来触发软中断。

那么,前面的中断服务程序是tasklet_action就开始执行了:

  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;  
  8.     __get_cpu_var(tasklet_vec).tail = &__get_cpu_var(tasklet_vec).head;  
  9.     local_irq_enable();  
  10.   
  11.     while (list) {  
  12.         struct tasklet_struct *t = list;  
  13.   
  14.         list = list->next;  
  15.   
  16.         if (tasklet_trylock(t)) {  
  17.             if (!atomic_read(&t->count)) {  
  18.                 if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))  
  19.                     BUG();  
  20.                 t->func(t->data);  
  21.                 tasklet_unlock(t);  
  22.                 continue;  
  23.             }  
  24.             tasklet_unlock(t);  
  25.         }  
  26.   
  27.         local_irq_disable();  
  28.         t->next = NULL;  
  29.         *__get_cpu_var(tasklet_vec).tail = t;  
  30.         __get_cpu_var(tasklet_vec).tail = &(t->next);  
  31.         __raise_softirq_irqoff(TASKLET_SOFTIRQ);  
  32.         local_irq_enable();  
  33.     }  
  34. }  

遍历tasklet_vec向量表,调用每个tasklet中在注册时的t->func(t->data);函数。

这样,tasklet就可以用于中断的顶半部和底半部。

在中断处理的顶半部,调用tasklet_schedule函数

Tasklet关联的tasklet处理函数就是中断的底半部处理。

相关内容