本文内核版本为2.6.32
软中断被执行的优先级要高于内核线程。硬中断是可以抢占内核线程的,硬中断退出时会立即执行软中断。这时软中断执行程序是运行在中断上下文的。如果软中断执行程序在指定时间内没处理完,就会挂起来等下次下次被执行。下次被执行可以是另一个硬中断退出时在中断上下文中执行,也可以是在特殊的内核线程ksoftirq被调度到来执行,这时是运行在线程上下文的。
总体来说,软中断执行程序被执行的机会会比普通线程要多。所以一些要优先并需要及时处理的工作可以交给软中断来处理。但linux 实现了软中断,但对内核模块开发的人员来说,内核并没有直接提供使用软中断的API。内核提供了tasklet 来给内核模块开发人员来用。
tasklet是在软中断HI_SOFTIRQ和TASKLET_SOFTIRQ基础上实现的。
初始化:
在start_kernel()里调用softirq_init()初始化这两个软中断。
数据结构
:
每个tasklet 由数据结构tasklet_struct 代表。
struct tasklet_struct
{
struct tasklet_struct *next;
unsigned long state; //tasklet状态。
atomic_t count; //锁计数器
void (*func)(unsigned long); //tasklet处理函数。
unsigned long data; //处理函数需要的参数。
};
全局数组tasklet_vec[NR_CPU] 和 tasklet_hi_vec[NR_CPU] 。数组元素是一个tasklet_head元素,是tasklet_struct 链表头。数组下标对应每个cpu ID.即数组保存了每个cpu上的tasklet_struct 链表。
tasklet 的state状态字段有如下状态:
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);
}
调用tasklet_init()来定义一个tasklet.
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_disable_nosync(struct tasklet_struct *t)
{
/*禁止tasklet后立即返回*/
atomic_inc(&t->count); //增加tasklet的锁计数器。
smp_mb__after_atomic_inc();
}
static inline void tasklet_unlock_wait(struct tasklet_struct *t)
{
/*直到等到tasklet执行完毕返回*/
while (test_bit(TASKLET_STATE_RUN, &(t)->state)) { barrier(); }
}
static inline void tasklet_disable(struct tasklet_struct *t)
{
/*禁止tasklet并且直到tasklet执行完毕后返回*/
tasklet_disable_nosync(t);
tasklet_unlock_wait(t);
smp_mb();
}
激活tasklet:
static inline void tasklet_enable(struct tasklet_struct *t)
{
smp_mb__before_atomic_dec();
/*递减tasklet的锁计数器*/
atomic_dec(&t->count);
}
调度tasklet
根据tasklet的优先级调用tasklet_schedule() 或tasklet_hi_schedule().
static inline voidtasklet_schedule(struct tasklet_struct *t)
{
/*如果tasklet没被调度过,即没被插入tasklet_vec相应的链表 ,调度*/
if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
{
__tasklet_schedule(t);
}
}
void fastcall__tasklet_schedule(struct tasklet_struct *t)
{
unsigned long flags;
/*保存中断状态寄存器并关闭本地CPU的中断*/
local_irq_save(flags);
/*把tasklet插入本地CPU的tasklet_vec中对应的链表里*/
t->next = __get_cpu_var(tasklet_vec).list;
__get_cpu_var(tasklet_vec).list = t;
/*把本地CPU的软中断TASKLET_SOFTIRQ位标记为挂起*/
raise_softirq_irqoff(TASKLET_SOFTIRQ);
/*恢复中断状态寄存器并开本地CPU中断*/
local_irq_restore(flags);
}
软中断TASKLET_SOFTIRQ的处理函数如下:
软中断被执行的优先级要高于内核线程。硬中断是可以抢占内核线程的,硬中断退出时会立即执行软中断。这时软中断执行程序是运行在中断上下文的。如果软中断执行程序在指定时间内没处理完,就会挂起来等下次下次被执行。下次被执行可以是另一个硬中断退出时在中断上下文中执行,也可以是在特殊的内核线程ksoftirq被调度到来执行,这时是运行在线程上下文的。
总体来说,软中断执行程序被执行的机会会比普通线程要多。所以一些要优先并需要及时处理的工作可以交给软中断来处理。但linux 实现了软中断,但对内核模块开发的人员来说,内核并没有直接提供使用软中断的API。内核提供了tasklet 来给内核模块开发人员来用。
tasklet是在软中断HI_SOFTIRQ和TASKLET_SOFTIRQ基础上实现的。
初始化:
在start_kernel()里调用softirq_init()初始化这两个软中断。
点击(此处)折叠或打开
- open_softirq(TASKLET_SOFTIRQ, tasklet_action,NULL);
- open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL);
每个tasklet 由数据结构tasklet_struct 代表。
struct tasklet_struct
{
struct tasklet_struct *next;
unsigned long state; //tasklet状态。
atomic_t count; //锁计数器
void (*func)(unsigned long); //tasklet处理函数。
unsigned long data; //处理函数需要的参数。
};
全局数组tasklet_vec[NR_CPU] 和 tasklet_hi_vec[NR_CPU] 。数组元素是一个tasklet_head元素,是tasklet_struct 链表头。数组下标对应每个cpu ID.即数组保存了每个cpu上的tasklet_struct 链表。
tasklet 的state状态字段有如下状态:
TASKLET_STATE_SCHED | 表示tasklet已经插入到tasklet_vec 或 tasklet_hi_vec数组中的一个链表上了。 |
TASKLET_STATE_RUN | 表示tasklet正在被执行。 |
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);
}
调用tasklet_init()来定义一个tasklet.
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_disable_nosync(struct tasklet_struct *t)
{
/*禁止tasklet后立即返回*/
atomic_inc(&t->count); //增加tasklet的锁计数器。
smp_mb__after_atomic_inc();
}
static inline void tasklet_unlock_wait(struct tasklet_struct *t)
{
/*直到等到tasklet执行完毕返回*/
while (test_bit(TASKLET_STATE_RUN, &(t)->state)) { barrier(); }
}
static inline void tasklet_disable(struct tasklet_struct *t)
{
/*禁止tasklet并且直到tasklet执行完毕后返回*/
tasklet_disable_nosync(t);
tasklet_unlock_wait(t);
smp_mb();
}
激活tasklet:
static inline void tasklet_enable(struct tasklet_struct *t)
{
smp_mb__before_atomic_dec();
/*递减tasklet的锁计数器*/
atomic_dec(&t->count);
}
调度tasklet
根据tasklet的优先级调用tasklet_schedule() 或tasklet_hi_schedule().
static inline voidtasklet_schedule(struct tasklet_struct *t)
{
/*如果tasklet没被调度过,即没被插入tasklet_vec相应的链表 ,调度*/
if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
{
__tasklet_schedule(t);
}
}
void fastcall__tasklet_schedule(struct tasklet_struct *t)
{
unsigned long flags;
/*保存中断状态寄存器并关闭本地CPU的中断*/
local_irq_save(flags);
/*把tasklet插入本地CPU的tasklet_vec中对应的链表里*/
t->next = __get_cpu_var(tasklet_vec).list;
__get_cpu_var(tasklet_vec).list = t;
/*把本地CPU的软中断TASKLET_SOFTIRQ位标记为挂起*/
raise_softirq_irqoff(TASKLET_SOFTIRQ);
/*恢复中断状态寄存器并开本地CPU中断*/
local_irq_restore(flags);
}
软中断TASKLET_SOFTIRQ的处理函数如下:
点击(此处)折叠或打开
- static void tasklet_action(struct softirq_action *a)
- {
- struct tasklet_struct *list;
- /*保存中断状态寄存器并关闭本地CPU的中断*/
-
- local_irq_disable();
-
- /*取得tasklet_vec数组中本地CPU的tasklet链表,并存入临时变量中*/
- list = __get_cpu_var(tasklet_vec).list;
-
- /*清空tasklet_vec数组中本地CPU的tasklet链表*/
- __get_cpu_var(tasklet_vec).list = NULL;
-
- /*恢复中断状态寄存器并开本地CPU中断*/
- local_irq_enable();
-
- /*循环执行tasklet链表上每个tasklet的处理函数*/
- while (list)
- {
- /*从链表上摘下一个tasklet*/
- struct tasklet_struct *t = list;
- list = list->next;
-
- /*如果tasklet没在被执行,执行,设置tasklet 的state字段为RUNNING状态*/
- if (tasklet_trylock(t))
- {
- /*如果tasklet的锁计数器为0,执行*/
- if (!atomic_read(&t->count))
- {
- /*清除tasklet的SCHED状态*/
- if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))
- BUG();
- /*执行tasklet的处理函数*/
- t->func(t->data);
- /*清除tasklet 的state字段的RUNNING状态,继续处理链表上的下一个tasklet*/
- tasklet_unlock(t);
- continue;
- }
-
- /*如果tasklet 的锁计数器不为0,表示tasklet被禁用,清除state字段的RUNNING状态*/
- tasklet_unlock(t);
- }
-
- /*关闭本地CPU中断,并把以上没被处理的tasklet重新挂到tasklet_vec数组中对应本地CPU上的链表上*/
- local_irq_disable();
- t->next = __get_cpu_var(tasklet_vec).list;
- __get_cpu_var(tasklet_vec).list = t;
- /*把本地CPU上的TASKLET_SOFTIRQ标记为挂起,并使能中断*/
- __raise_softirq_irqoff(TASKLET_SOFTIRQ);
- local_irq_enable();
- }
- }