linux内核中的tasklet机制
tasklet是IO驱动程序中实现可延迟函数的一种首选的方法,一个tasklet只可以在一个CPU上同步地执行,不同的tasklet可以在不同地CPU上同步地执行。
tasklet的实现是建立在两个软件中断的基础之上的,即HI_SOFTIRQ和TASKLET_SOFTIRQ,本质上没有什么区别,只不过HI_SOFTIRQ的优先级更高一些,建立在HI_SOFTIRQ上的tasklet会早于TASKLET_SOFTIRQ执行。
tasklet链表是存放在tasklet_vec和tasklet_hi_vec这两个数组里面的,数组的长度就是cpu的个数,这是两个percpu数组。linux为smp定义了一系列的宏用于初始化获取删除percpu变量
1 | DEFINE_PER_CPU(struct tasklet_struct , tasklet_vec) |
这个宏的作用是创建一个类型为tasklet_struct类型的tasklet_vec数组,这个数组即属于percpu数据,具体的实现机制我暂时还没有搞明白。
1 | __per_cpu_var(tasklet_vec) |
根据percpu变量的名字获取percpu数据
创建一个tasklet首先需要创建一个tasklet描述符,即struct tasklet_struct结构体,并用tasklet_init()函数初始化它。
@include/linux/interput.h
1 2 3 4 5 6 7 8 | struct tasklet_struct { struct tasklet_struct *next; unsigned long state; atomic_t count; void (*func)(unsigned long); unsigned long data; }; |
其中count是一个原子变量的锁时数器,大于0时表示当前tasklet被禁止,tasklet_enable()和tasklet_disable()函数通过减少和增加这个字段的值来实现当前tasklet的启用和禁止。
state是tasklet的状态标志,只有两个选项:
@include/linux/interput.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | enum { TASKLET_STATE_SCHED, /* tasklet已经被调度,但还尚未执行 */ TASKLET_STATE_RUN /* tasklet正在执行,只针对SMP有效,单处理器无意义 */ }; void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data) { t->next = NULL; t->state = 0; atomic_set(&t->count, 0); t->func = func; t->data = data; } |
在创建完tasklet描述符之后就需要对tasklet进行调度,即把tasklet描述符插入到tasklet_vec中相应链表中去。调度函数如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | void __tasklet_schedule(struct tasklet_struct *t) { unsigned long flags; /* 在flasgs中保存eflags寄存器的IF标志,并关中段 */ local_irq_save(flags); t->next = NULL; /* 将tasklet描述符插入到tasklet_vec链表的尾部 */ *__get_cpu_var(tasklet_vec).tail = t; __get_cpu_var(tasklet_vec).tail = &(t->next); /* 激活TASKLET_SOFTIRQ软中断 */ raise_softirq_irqoff(TASKLET_SOFTIRQ); /* 恢复eflags寄存器的值,并打开中断 */ local_irq_restore(flags); } |
至此tasklet的调度就完成了,对tasklet可延迟函数的处理将交由软中断处理函数处理,下面看TASKLET_SOFTIRQ软中断的处理函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | static void tasklet_action(struct softirq_action *a) { struct tasklet_struct *list; /* 禁用本地中断 */ local_irq_disable(); /* 将tasket_vec中对应的tasklet链表头的地址保存在list临时变量中 * 并清空tasklet_vec中的链表,以允许新的tasklet */ list = __get_cpu_var(tasklet_vec).head; __get_cpu_var(tasklet_vec).head = NULL; __get_cpu_var(tasklet_vec).tail = &__get_cpu_var(tasklet_vec).head; /* 重新启用本地中断 */ local_irq_enable(); while (list) { struct tasklet_struct *t = list; list = list->next; if (tasklet_trylock(t)) { if (!atomic_read(&t->count)) { if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state)) BUG(); t->func(t->data); tasklet_unlock(t); continue; } tasklet_unlock(t); } local_irq_disable(); t->next = NULL; *__get_cpu_var(tasklet_vec).tail = t; __get_cpu_var(tasklet_vec).tail = &(t->next); __raise_softirq_irqoff(TASKLET_SOFTIRQ); local_irq_enable(); } } |
原创文章,转载请注明: 转载自basic coder
Recent Comments