linux中断延迟之tasklet

转自:http://blog.csdn.net/bullbat/article/details/7101140

 

tasklet是I/O驱动程序中实现可延迟函数的首选方法。从下面的内核代码的分析中我们会看到,tasklet建立在两个叫做HI_SOFTIRQ和TASKLET_SOFTIRQ的软中断之上。几个tasklet可以与同一个软中断相关联,每个tasklet执行自己的函数。tasklet和高优先级的tasklet分别存放在tasklet_vec和tasklet_hi_vec数组中。下面我们结合具体的代码来了解他的实现和运用。

tasklet的内核实现

在start_kernel函数做内核初始化工作的时候会调用函数softirq_init

  1. void __init softirq_init(void
  2.     int cpu; 
  3.  
  4.     for_each_possible_cpu(cpu) { 
  5.         int i; 
  6.         /*对tasklet相关pcp变量的初始化*/ 
  7.         per_cpu(tasklet_vec, cpu).tail = 
  8.             &per_cpu(tasklet_vec, cpu).head; 
  9.         per_cpu(tasklet_hi_vec, cpu).tail = 
  10.             &per_cpu(tasklet_hi_vec, cpu).head; 
  11.         for (i = 0; i < NR_SOFTIRQS; i++) 
  12.             INIT_LIST_HEAD(&per_cpu(softirq_work_list[i], cpu)); 
  13.     } 
  14.  
  15.     register_hotcpu_notifier(&remote_softirq_cpu_notifier); 
  16.     /*将tasklet 执行函数加入软中断向量中,
  17.     这个执行函数会执行tasklet对应一个链表
  18.     中的所有函数*/ 
  19.     open_softirq(TASKLET_SOFTIRQ, tasklet_action); 
  20.     open_softirq(HI_SOFTIRQ, tasklet_hi_action); 
void __init softirq_init(void)
{
	int cpu;

	for_each_possible_cpu(cpu) {
		int i;
		/*对tasklet相关pcp变量的初始化*/
		per_cpu(tasklet_vec, cpu).tail =
			&per_cpu(tasklet_vec, cpu).head;
		per_cpu(tasklet_hi_vec, cpu).tail =
			&per_cpu(tasklet_hi_vec, cpu).head;
		for (i = 0; i < NR_SOFTIRQS; i++)
			INIT_LIST_HEAD(&per_cpu(softirq_work_list[i], cpu));
	}

	register_hotcpu_notifier(&remote_softirq_cpu_notifier);
	/*将tasklet 执行函数加入软中断向量中,
	这个执行函数会执行tasklet对应一个链表
	中的所有函数*/
	open_softirq(TASKLET_SOFTIRQ, tasklet_action);
	open_softirq(HI_SOFTIRQ, tasklet_hi_action);
}

open_softirq函数在前面我们已经分析过了,在这里可以看出,两类tasklet以一个软中断的方式加入软中断向量中,而这两种tasklet实际上位两个链表,就是上面的tasklet_hi_vec和tasklet_vec,我们看一个就行了,实现大同小异。

  1. static void tasklet_action(struct softirq_action *a) 
  2.     struct tasklet_struct *list; 
  3.  
  4.     local_irq_disable();/*禁用本地中断*/ 
  5.     list = __get_cpu_var(tasklet_vec).head;/*将链表的地址保存*/ 
  6.     __get_cpu_var(tasklet_vec).head = NULL;/*已调度的tasklet描述符的链表被清空*/ 
  7.     __get_cpu_var(tasklet_vec).tail = &__get_cpu_var(tasklet_vec).head; 
  8.     local_irq_enable();/*打开本地中断*/ 
  9.  
  10.     while (list) {/*对于list链表的每个tasklet描述符*/ 
  11.         struct tasklet_struct *t = list; 
  12.  
  13.         list = list->next; 
  14.  
  15.         if (tasklet_trylock(t)) { 
  16.             /*通过查看tasklet描述符的count字段,
  17.             检查tasklet是否被禁止*/ 
  18.             if (!atomic_read(&t->count)) {/*如果没有禁止*/ 
  19.                 /*清楚调度标志*/ 
  20.                 if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state)) 
  21.                     BUG(); 
  22.                 t->func(t->data);/*执行对应函数*/ 
  23.                 tasklet_unlock(t); 
  24.                 continue;/*继续下一个*/ 
  25.             } 
  26.             tasklet_unlock(t); 
  27.         } 
  28.         /*运行到这里,表示tasklet被禁止*/ 
  29.         local_irq_disable(); 
  30.         t->next = NULL; 
  31.         /*重新插入到链表中,然后再次激活软中断*/ 
  32.         *__get_cpu_var(tasklet_vec).tail = t; 
  33.         __get_cpu_var(tasklet_vec).tail = &(t->next); 
  34.         /*这里的激活实际上设置位掩码pending
  35.         的对应位,使其在软中断时能够被执行*/ 
  36.         __raise_softirq_irqoff(TASKLET_SOFTIRQ); 
  37.         local_irq_enable(); 
  38.     } 
static void tasklet_action(struct softirq_action *a)
{
	struct tasklet_struct *list;

	local_irq_disable();/*禁用本地中断*/
	list = __get_cpu_var(tasklet_vec).head;/*将链表的地址保存*/
	__get_cpu_var(tasklet_vec).head = NULL;/*已调度的tasklet描述符的链表被清空*/
	__get_cpu_var(tasklet_vec).tail = &__get_cpu_var(tasklet_vec).head;
	local_irq_enable();/*打开本地中断*/

	while (list) {/*对于list链表的每个tasklet描述符*/
		struct tasklet_struct *t = list;

		list = list->next;

		if (tasklet_trylock(t)) {
			/*通过查看tasklet描述符的count字段,
			检查tasklet是否被禁止*/
			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);
		}
		/*运行到这里,表示tasklet被禁止*/
		local_irq_disable();
		t->next = NULL;
		/*重新插入到链表中,然后再次激活软中断*/
		*__get_cpu_var(tasklet_vec).tail = t;
		__get_cpu_var(tasklet_vec).tail = &(t->next);
		/*这里的激活实际上设置位掩码pending
		的对应位,使其在软中断时能够被执行*/
		__raise_softirq_irqoff(TASKLET_SOFTIRQ);
		local_irq_enable();
	}
}

可以看到,tasklet其实是软中断中两项,每一项对应的不是一个软中断函数,而是一个链表上的所有函数,在对应的软中断到来时,对应链表中的所有函数都将得到执行。而对于tasklet的唤醒其实就是设置pending位掩码的相应位,使软中断到来时会执行他。

tasklet的内核编程与应用

了解了tasklet的内核实现,对于他的应用就很简单了,首先,你应该分配一个新的tasklet_struct数据结构,并调用tasklet_init函数初始化它。

  1. void tasklet_init(struct tasklet_struct *t, 
  2.           void (*func)(unsigned long), unsigned long data) 
  3.     t->next = NULL; 
  4.     t->state = 0; 
  5.     atomic_set(&t->count, 0); 
  6.     t->func = func; 
  7.     t->data = data; 
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_enable函数;

为了激活tasklet,根据自己tasklet需要的优先级,调用tasklet_schedule函数或tasklet_hi_schedule函数。我们也只看一个

  1. static inline void tasklet_hi_schedule(struct tasklet_struct *t) 
  2.     /*标志位的检查*/ 
  3.     if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) 
  4.         __tasklet_hi_schedule(t); 
static inline void tasklet_hi_schedule(struct tasklet_struct *t)
{
	/*标志位的检查*/
	if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
		__tasklet_hi_schedule(t);
}
  1. void __tasklet_hi_schedule(struct tasklet_struct *t) 
  2.     unsigned long flags; 
  3.  
  4.     local_irq_save(flags); 
  5.     t->next = NULL; 
  6.     *__get_cpu_var(tasklet_hi_vec).tail = t; 
  7.     __get_cpu_var(tasklet_hi_vec).tail = &(t->next); 
  8.     /*激活对应softirq_vec[]数组中HI_SOFTIRQ下标
  9.     的软中断,就是tasklet_hi_vec链表*/ 
  10.     raise_softirq_irqoff(HI_SOFTIRQ); 
  11.     local_irq_restore(flags); 
void __tasklet_hi_schedule(struct tasklet_struct *t)
{
	unsigned long flags;

	local_irq_save(flags);
	t->next = NULL;
	*__get_cpu_var(tasklet_hi_vec).tail = t;
	__get_cpu_var(tasklet_hi_vec).tail = &(t->next);
	/*激活对应softirq_vec[]数组中HI_SOFTIRQ下标
	的软中断,就是tasklet_hi_vec链表*/
	raise_softirq_irqoff(HI_SOFTIRQ);
	local_irq_restore(flags);
}

实现了上面的几个操作流程,当软中断函数一旦被唤醒就由do_softirq函数来执行。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值