首页 > Linux > linux内核中的tasklet机制

linux内核中的tasklet机制

2010年6月6日 发表评论 阅读评论 3,348 人阅读过  

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

本文链接地址: http://basiccoder.com/tasket-in-linux-kernel.html

分类: Linux 标签:
  1. 本文目前尚无任何评论.
注意: 评论者允许使用'@user空格'的方式将自己的评论通知另外评论者。例如, ABC是本文的评论者之一,则使用'@ABC '(不包括单引号)将会自动将您的评论发送给ABC。使用'@all ',将会将评论发送给之前所有其它评论者。请务必注意user必须和评论者名相匹配(大小写一致)。