《深入理解Linux内核》第四章 中断和异常(四)

上篇讲完了softirq,今天让我们来看一个softirq应用实例-------tasklet

二、tasklet

1、优点

1、简单易用,在设备驱动中比较常见

2、同softirq一样,tasklet执行期间,中断处于使能

2、特点

1、动态分配

2、同一种tasklet不能同时在多个CPU上运行,但是不同的tasklet,可以同时在多个CPU上运行

3、tasklet种类

1、HI_SOFTIRQ,对应0号softirq

2、TASKLET_SOFTIRQ,对应5号softirq

两者除了调用顺序有先后外(先调用HI_SOFTIRQ,再调用TASKLET_SOFTIRQ),其余均相同

4、数据结构

/* Tasklets --- multithreaded analogue of BHs.

   Main feature differing them of generic softirqs: tasklet
   is running only on one CPU simultaneously.

   Main feature differing them of BHs: different tasklets
   may be run simultaneously on different CPUs.

   Properties:
   * If tasklet_schedule() is called, then tasklet is guaranteed
     to be executed on some cpu at least once after this.
   * If the tasklet is already scheduled, but its excecution is still not
     started, it will be executed only once.
   * If this tasklet is already running on another CPU (or schedule is called
     from tasklet itself), it is rescheduled for later.
   * Tasklet is strictly serialized wrt itself, but not
     wrt another tasklets. If client needs some intertask synchronization,
     he makes it with spinlocks.
 */

struct tasklet_struct
{
	struct tasklet_struct *next;
	unsigned long state;
	atomic_t count;
	void (*func)(unsigned long);
	unsigned long data;
};

/* Tasklets */
struct tasklet_head
{
	struct tasklet_struct *list;
};


static DEFINE_PER_CPU(struct tasklet_head, tasklet_vec) = { NULL };
static DEFINE_PER_CPU(struct tasklet_head, tasklet_hi_vec) = { NULL }

5、使用方法

1、动态分配tasklet_struct

2、tasklet_init()

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_struct结构体中各个字段 

3、tasklet_schedule()

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);
}

EXPORT_SYMBOL(__tasklet_schedule)

static inline void tasklet_schedule(struct tasklet_struct *t)
{
	if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
		__tasklet_schedule(t);
}

1、测试tasklet是否处于TASKLET_STATE_SCHED状态,如果没有处于,则置位state中TASKLET_STATE_SCHED bit,否则直接退出

2、将tasklet_struct结构体指针t赋值给当前CPU对应tasklet_vec中list字段

3、raise_softirq_irqoff()具体实现请参考https://blog.csdn.net/yxw0609131056/article/details/110004548 《深入理解Linux内核》第四章中断和异常(三),将当前CPU对应的irq_cpustat_t中__softirq_pending字段对应的bit TASKLET_SOFTIRQ置位。

4、当系统运行到softirq检查点时,就会调用do_softirq()

do_softirq()具体实现请参考https://blog.csdn.net/yxw0609131056/article/details/110004548 《深入理解Linux内核》第四章中断和异常(三)

5、在__do_softirq()中,就会调用h->action(h),

原来系统在初始化时,已经将action()指定为 tasklet_action()

void __init softirq_init(void)
{
	open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL);
	open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL);
}

6、tasklet_action()

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();
	}
}

1、首先获得当前CPU对应tasklet_vec中list字段,赋值给list变量,并将其清空

2、如果list不为空,则说明有tasklet请求产生,需要进一步处理,否则直接退出

3、判断tasklet是否处于TASKLET_STATE_RUN并测试tasklet_struct结构体count字段,如果成功,则清除state中TASKLET_STATE_SCHED bit,否则直接跳到6

4、调用用户注册的func()

5、如果前面tasklet处于TASKLET_STATE_RUN,则清除TASKLET_STATE_RUN bit

6、重新设置当前CPU对应tasklet_vec中list字段,重新产生TASKLET_SOFTIRQ

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值