Linuxtaskletstudy
一、tasklet作用
二、tasklet相关的数据结构
structtasklet_struct
{
structtasklet_struct*next;
unsignedlongstate;
atomic_tcount;
void(*func)(unsignedlong);
unsignedlongdata;
};
/*Tasklets*/
structtasklet_head
{
structtasklet_struct*head;
structtasklet_struct**tail;
};
该结构即为tasklet的主要数据结构,其中
state:保存tasklet的状态,判断当前tasklet是否已运行、对该tasklet进行加锁操作等,
count:tasklet的开启与关闭。0:enable1:disable
void(*func)(unsignedlong):该tasklet对应的回调处理函数,当我们使用tasklet时,该
函数即需要我们自己定义,用于处理底半部的相关事宜
data:回调处理函数的传参
三、tasklet初始化、
tasklet的初始化有2个函数:
1)tasklet_init
voidtasklet_init(structtasklet_struct*t,
void(*func)(unsignedlong),unsignedlongdata)
{
t->next=NULL;
t->state=0;
atomic_set(&t->count,0);
t->func=func;
t->data=data;
}
该函数主要实现tasklet的初始化,主要是对func、data、count进行赋值
2)DECLARE_TASKLET
#defineDECLARE_TASKLET(name,func,data)\
structtasklet_structname={NULL,0,ATOMIC_INIT(0),func,data}
该宏完成一个tasklet的定义与初始化,可谓一步到位。
四、tasklet对应的软中断注册
因为tasklet是基于软中断的,所以在使用tasklet之前,肯定需要先注册相应的软中断。
在softirq_init,注册了2个软中断TASKLET_SOFTIRQ、HI_SOFTIRQ,这2个软中断即是tasklet对应的(不同的优先级),
五、tasklet的加锁相关的函数
#ifdefCONFIG_SMP
staticinlineinttasklet_trylock(structtasklet_struct*t)
{
return!test_and_set_bit(TASKLET_STATE_RUN,&(t)->state);
}
staticinlinevoidtasklet_unlock(structtasklet_struct*t)
{
smp_mb__before_clear_bit();
clear_bit(TASKLET_STATE_RUN,&(t)->state);
}
staticinlinevoidtasklet_unlock_wait(structtasklet_struct*t)
{
while(test_bit(TASKLET_STATE_RUN,&(t)->state)){barrier();}
}
#else
#definetasklet_trylock(t)1
#definetasklet_unlock_wait(t)do{}while(0)
#definetasklet_unlock(t)do{}while(0)
#endif
在SMP下,上锁函数tasklet_trylock主要是设置tasklet->state的TASKLET_STATE_RUN位,将该位置为1,上锁的主要功能是保证SMP下在同一时刻只有一个CPU执行相同的tasklet操作。在非SMP下tasklet_trylock不起作用。
六、tasklet的启用
tasklet_schedule
作用:
1)首先判断tasklet->state的TASKLET_STATE_SCHED位是否置位,在未置位的情况下,设置该位,并调用__tasklet_schedule(t),将该tasklet加入到表头tasklet_vec中
staticinlinevoidtasklet_schedule(structtasklet_struct*t)
{
if(!test_and_set_bit(TASKLET_STATE_SCHED,&t->state))
__tasklet_schedule(t);
}
__tasklet_schedule:
作用:
1、关闭中断
2、调用__tasklet_common_schedule,实现添加tasklet并开启软中断的功能
3、开启中断
void__tasklet_schedule(structtasklet_struct*t)
{
unsignedlongflags;
local_irq_save(flags);
__tasklet_common_schedule(t,&__get_cpu_var(tasklet_vec),TASKLET_SOFTIRQ);
local_irq_restore(flags);
}
__tasklet_common_schedule:
作用:
1)对该tasklet进行上锁操作,在上锁成功以后,执行下面操作
2)判断当前tasklet的状态是否为TASKLET_STATE_SCHED,
若是则:
a)将tasklett添加到tasklet_head中
b)调用raise_softirq_irqoff,开启tasklet对应的软中断
c)解除tasklet的锁
若不是:
a)调用tasklet_tryunlock,执行解锁操作。
staticvoidinline
__tasklet_common_schedule(structtasklet_struct*t,structtasklet_head*head,unsignedintnr)
{
if(tasklet_trylock(t)){
again:
/*Wemayhavebeenpreemptedbeforetasklet_trylock
*and__tasklet_actionmayhavealreadyrun.
*Sodoublechecktheschedbitwhilethetakslet
*islockedbeforeaddingittothelist.
*/
if(test_bit(TASKLET_STATE_SCHED,&t->state)){
t->next=NULL;
*head->tail=t;
head->tail=&(t->next);
raise_softirq_irqoff(nr);
tasklet_unlock(t);
}else{
/*Thisissubtle.Ifwehitthecornercaseabove
*Itispossiblethatwegetpreemptedrighthere,
*andanothertaskhassuccessfullycalled
*tasklet_schedule(),thenthisfunction,and
*failedonthetrylock.Thuswemustbesure
*beforereleasingthetaskletlock,thatthe
*SCHED_BITisclear.Otherwisethetasklet
*maygetitsSCHED_BITset,butnotaddedtothe
*list
*/
if(!tasklet_tryunlock(t))
gotoagain;
}
}
}
以上3个函数实现了将tasklet添加到tasklet_vec(每个CPU都有一个该变量)中,并开启软中断。
七tasklet的执行
tasklet是依托软中断的,那么tasklet的回调处理函数也一定是在软中断TASKLET_SOFTIRQ、HI_SOFTIRQ的处理函数里调用的。对于软中断TASKLET_SOFTIRQ,其中断处理函数的定义如下:
tasklet_action:
作用:
1)关闭软中断,获取运行CPU所对应的tasklet链表的表头,然后将表头置为NULL,再启中断
2)遍历tasklet链表,每次遍历均执行如下操作:
a)获取tasklet链表的一个tasklet变量
b)对该tasklet执行加锁操作,即置位TASKLET_STATE_RUN
c)判断当前tasklet是否使能,若已使能,则执行以下操作
i.tasklet的当前状态若为TASKLET_STATE_SCHED,则清空该位
ii.调用该tasklet的回调处理函数
iii.解锁该tasklet,重新while循环
若未使能,则执行以下操作:
i.关闭中断
ii.将该tasklet重新加入到链表tasklet_vec
iii.开启软中断TASKLET_SOFTIRQ,在下一次处理该软中断时,再处理该tasklet
iv.开启中断
staticvoidtasklet_action(structsoftirq_action*a)
{
structtasklet_struct*list;
local_irq_disable();
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){
structtasklet_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();
}
}
问:在同一时刻,多个CPU不能同时执行同一个tasklet的功能是如何实现的
在tasklet的数据结构中,有一个成员选项state,该变量目前有意义的2位分别是bit0、bit1位,其中bit1位TASKLET_STATE_RUN即用于SMP,主要就是用来防止多个CPU同时执行同一个tasklet的。
再怎么风光明媚的自家山川,