首页 > 代码库 > tasklet浅析
tasklet浅析
tasklet是在HI_SOFTIRQ和TASKLET_SOFTIRQ两个软中断的基础上实现的(它们是在同一个源文件中实现,由此可见它们的关系密切程度),它的数据结构和软中断比较相似,这篇博文将分析tasklet的初始化过程。
1.和tasklet相关的数据结构
tasklet_vec和tasklet_hi_vec数组(kernel/softirq.c)
1 static DEFINE_PER_CPU(struct tasklet_head, tasklet_vec);2 static DEFINE_PER_CPU(struct tasklet_head, tasklet_hi_vec);
第1行中为所有cpu创建了名为tasklet_vec数组,每个元素和一个cpu相关联,数组元素类型为struct tasklet_head。第2行和第1行类似,创建出的tasklet_hi_vec数组存放的是每个cpu上优先级更高的tasklet小任务。下面看下struct tasklet_head结构体(kernel/softirq.c)。
1 struct tasklet_head {2 struct tasklet_struct *head;3 struct tasklet_struct **tail;4 };
内核会为每个cpu分配一个这样的结构体变量,从而每个cpu都拥有一个小任务函数的链表。再看下结构体(kernel/softirq.c)。
1 struct tasklet_struct2 {3 struct tasklet_struct *next;4 unsigned long state;5 atomic_t count;6 void (*func)(unsigned long);7 unsigned long data;8 };
第1行next指向小任务链表下一个节点,第6行func变量指向了小任务函数。
2.tasklet初始化
小任务是基于HI_SOFTIRQ和TASKLET_SOFTIRQ两个软中断来实现的。HI_SOFTIRQ所对应的软中断函数是tasklet_action,如下(kernel/softirq.c):
1 static void tasklet_action(struct softirq_action *a) 2 { 3 struct tasklet_struct *list; 4 5 local_irq_disable(); 6 list = __this_cpu_read(tasklet_vec.head); 7 __this_cpu_write(tasklet_vec.head, NULL); 8 __this_cpu_write(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,19 &t->state))20 BUG();21 t->func(t->data);22 tasklet_unlock(t);23 continue;24 }25 tasklet_unlock(t);26 }27 28 local_irq_disable();29 t->next = NULL;30 *__this_cpu_read(tasklet_vec.tail) = t;31 __this_cpu_write(tasklet_vec.tail, &(t->next));32 __raise_softirq_irqoff(TASKLET_SOFTIRQ);33 local_irq_enable();34 }35 }
第6行中tasklet_vec数组中和本地cpu相关的元素的head域值,保存在list变量中,实际上把当前cpu的tastlet任务链表卸下来,list成为链表头指针。第7,8行把tasklet_vec数组和本地cpu相关的元素的head域置为NULL,tail域指向head域。第11-26行用while循环执行所有的tasklet函数。第21行是tasklet函数的执行。第30-31行把执行之后的struct tasklet_struct链表节点重新挂到tasklet_vec数组中,由此可见装tasklet函数的结构体不用程序员手动创建,由系统来提供。第32行重新设置软中断掩码表的相应位。TASKLET_SOFTIRQ所对应的软中断函数是tasklet_hi_action,和tasklet_action执行过程类似,不再分析。
来看下tasklet_action函数初始化过程(kernel/softirq.c)。
1 void __init softirq_init(void) 2 { 3 int cpu; 4 5 for_each_possible_cpu(cpu) { 6 per_cpu(tasklet_vec, cpu).tail = 7 &per_cpu(tasklet_vec, cpu).head; 8 per_cpu(tasklet_hi_vec, cpu).tail = 9 &per_cpu(tasklet_hi_vec, cpu).head;10 }11 12 open_softirq(TASKLET_SOFTIRQ, tasklet_action);13 open_softirq(HI_SOFTIRQ, tasklet_hi_action);14 }
第5-10行,初始化tasklet_vec和tasklet_hi_vec两个数组的元素,使每个元素的tail域指向head域。cpu编号作为两个数组的下标。第12-13行将负责处理tasklet任务的两个软中断函数tasklet_action和tasklet_hi_action分别存入软中断数组softirq_vec[NR_SOFTIRQS]的相应元素中。来看下open_softirq函数(kernel/softirq.c)。
1 void open_softirq(int nr, void (*action)(struct softirq_action *))2 {3 softirq_vec[nr].action = action;4 }
相信你一看就明白了,不用解释了~
再看看tasklet的初始化过程(kernel/softirq.c):
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 =http://www.mamicode.com/ data;9 }
该函数接收tasklet_struct结构体指针和小任务函数指针,然后对tasklet_struct结构体进行初始化,并将函数指针赋给t->func域。tasklet_init函数在tasklet_hrtimer_init函数中被调用。
至此,tasklet的初始化就分析完了。