参考:
《Linux内核设计与实现》
http://blog.csdn.net/fontlose/article/details/8279113
http://blog.chinaunix.net/uid-27212029-id-3386692.html
tasklet是中断处理下半部分最常用的一种方法,驱动程序一般先申请中断,在中断处理函数内完成中断上半部分的工作后调用tasklet。tasklet有如下特点:
1.tasklet只可以在一个CPU上同步地执行,不同的tasklet可以在不同地CPU上同步地执行。
2.tasklet的实现是建立在两个软件中断的基础之上的,即HI_SOFTIRQ和TASKLET_SOFTIRQ,本质上没有什么区别,只不过HI_SOFTIRQ的优先级更高一些
3.由于tasklet是在软中断上实现的,所以像软中断一样不能睡眠、不能阻塞,处理函数内不能含有导致睡眠的动作,如减少信号量、从用户空间拷贝数据或手工分配内存等。
4.一个 tasklet 能够被禁止并且之后被重新使能; 它不会执行直到它被使能的次数与被禁止的次数相同.
5.tasklet的串行化使tasklet函数不必是可重入的,因此简化了设备驱动程序开发者的工作。
6.每个cpu拥有一个tasklet_vec链表,具体是哪个cpu的tasklet_vec链表,是根据当前线程是运行在哪个cpu来决定的。
tasklet是驱动程序实现可延迟函数的首选方法,tasklet建立在HI_SOFTIRT和TASKLET_SOFTIRQ两个软中断上。
原理
tasklet和高优先级的tasklet分别存放在tasklet_vec和tasklet_hi_vec数组中,二者都包含类型为tasklet_head的
NR_CPUS个元素,每个元素都是指向tasklet描述符链表的指针。
执行过程
HI_SOFTIRQ软中断相关的软中断函数是tasklet_hi_action(),而与TASKLET_SOFTIRQ相关的函数是tasklet_action()
1.禁止本地中断
2.获得本地CPU的逻辑号n
3.把tasklet_vec[n]或tasklet_hi_vec[n]所指向的链表的地址存入局部变量list
4.把tasklet_vec[n]或tasklet_hi_vec[n]的值赋为NULL,因此已调度的tasklet描述符链表被清空
5.打开本地中断
6.对于list所指向的每个tasklet描述符
a.在多处理器系统上,检查tasklet的TASKLET_STATE_RUN标志。
if标志被设置,list重新插入结构数组,并激活TASKLET_SOFTIRQ或HI_SOFTIRQ软中断,这个tasklet
被延迟
else 设置TASKLET_STATE_RUN标志,以便tasklet不能在其他CPU上运行
b.通过查看tasklet描述符的count字段,看tasklet是否被禁止。如果是清TASKLET_STATE_RUN标志,把
list重新插入结构数组,并激活相应的软中断。
c.如果tasklet被激活,清TASKLET_STATE_SCHED标志,并执行tasklet函数
编写一个设备驱动程序的步骤
1.分配一个新的tasklet_struct数据结构,并用tasklet_init()初始化它;
2.实现tasklet函数
3.禁止或使能tasklet
tasklet结构体
-
struct tasklet_struct
-
{
-
struct tasklet_struct *next;
-
unsigned long state;
-
atomic_t count;
-
void (*func)(unsigned long);
-
unsigned long data;
-
};
-
-
tasklet结构变量是tasklet_vec链表的一个节点,next是链表的下一节点,state使用了两个位如下
-
enum
-
{
-
TASKLET_STATE_SCHED,
-
TASKLET_STATE_RUN
-
};
-
-
count用于禁止使能,每禁止一次计数加一,没使能一次计数减一,只有禁止次数和使能次数一样(count等于0)时tasklet才会执行调用函数。
-
func 执行函数不能有导致睡眠、不能阻塞的代码。
-
data 执行函数的参数
tasklet的定义
-
定义时初始化
-
定义变量名为name的tasklets_struct变量,并初始化调用函数为func,参数为data,使能tasklet
-
DECLARE_TASKLET(name, func, data); #define DECLARE_TASKLET(name, func, data) \
-
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }
-
-
定义变量名为name的tasklets_struct变量,并初始化调用函数为func,参数为data,禁止tasklet
-
DECLARE_TASKLET_DISABLED(name, func, data);
-
#define DECLARE_TASKLET_DISABLED(name, func, data) \
-
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data }
-
-
运行中初始化 先定义 struct tasklet_struct name ;
-
后初始化
-
-
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的调用过程
-
static inline void tasklet_schedule(struct tasklet_struct *t);使用此函数即可完成调用
-
static inline void tasklet_schedule(struct tasklet_struct *t)
-
{
-
-
if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
-
__tasklet_schedule(t);
-
}
-
-
-
void fastcall __tasklet_schedule(struct tasklet_struct *t)
-
{
-
unsigned long flags;
-
local_irq_save(flags);
-
t->next = __get_cpu_var(tasklet_vec).list;
-
__get_cpu_var(tasklet_vec).list = t;
-
raise_softirq_irqoff(TASKLET_SOFTIRQ);
-
local_irq_restore(flags);
-
}
-
至此调度函数已经触发了一个软中断,具体中断函数看tasklet的初始化
-
void __init softirq_init(void)
-
{
-
open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL);
-
open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL);
-
}
-
-
-
static void tasklet_action(struct softirq_action *a)
-
{
-
struct tasklet_struct *list;
-
-
local_irq_disable();
-
list = __get_cpu_var(tasklet_vec).list;
-
__get_cpu_var(tasklet_vec).list = NULL;
-
-
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 = __get_cpu_var(tasklet_vec).list;
-
__get_cpu_var(tasklet_vec).list = t;
-
__raise_softirq_irqoff(TASKLET_SOFTIRQ);
-
local_irq_enable();
-
}
-
}
相关函数
-
-
static inline void tasklet_disable_nosync(struct tasklet_struct *t)
-
{
-
atomic_inc(&t->count);
-
smp_mb__after_atomic_inc();
-
}
-
-
-
static inline void tasklet_disable(struct tasklet_struct *t){
-
tasklet_disable_nosync(t);
-
tasklet_unlock_wait(t);
-
smp_mb();
-
}
-
-
static inline int tasklet_trylock(struct tasklet_struct *t){
-
return !test_and_set_bit(TASKLET_STATE_RUN, &(t)->state);
-
}
-
-
static inline void tasklet_unlock(struct tasklet_struct *t){
-
smp_mb__before_clear_bit();
-
clear_bit(TASKLET_STATE_RUN, &(t)->state);
-
}
-
-
static inline void tasklet_unlock_wait(struct tasklet_struct *t){
-
while (test_bit(TASKLET_STATE_RUN, &(t)->state)) {
-
barrier();
-
}
-
}
-
-
-
static inline void tasklet_enable(struct tasklet_struct *t)
-
{
-
smp_mb__before_atomic_dec();
-
atomic_dec(&t->count);
-
}
-
-
-
void tasklet_hi_schedule(struct tasklet_struct *t);
-
-
-
void tasklet_kill(struct tasklet_struct *t)
-
{
-
if (in_interrupt())
-
printk("Attempt to kill tasklet from interrupt\n");
-
-
while (test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) {
-
do
-
yield();
-
while (test_bit(TASKLET_STATE_SCHED, &t->state));
-
}
-
tasklet_unlock_wait(t);
-
clear_bit(TASKLET_STATE_SCHED, &t->state);
-
}
-
这个函数不是真的去杀掉被调度的tasklet,而是保证tasklet不再调用
linux中断处理下文:软中断tasklet机制分析
原文:http://blog.csdn.net/hustyangju/article/details/40350145