Linux驱动之tasklets和workqueue

tasklet是一个数据结构

它指导内核在稍候的空闲时间(不确定) 使用用户定义的参数,执行用户定义的函数

运行在软中断上下文,由内核线程软中断执行调度,因此不能使用sleep

只能运行在一个cpu上

数据结构:

struct tasklet_struct
{
	struct tasklet_struct *next;
	unsigned long state;
	atomic_t count;
	void (*func)(unsigned long);
	unsigned long data;
};
初始化方法:

tasklet_init(struct tasklet_struct * t,void(* func)(unsigned long),unsigned long data)
DECLARE_TASKLET(name,func,data)
DECLARE_TASKLET_DISABLED(name,func,data)
执行调度:

static inline void tasklet_schedule(struct tasklet_struct *t)
{
	if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
		__tasklet_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);
}

调度的结果将tasklet加入系统列表,执行完毕后,从列表中删除。若需要再次运行,需要再次将tasklet加入系统列表中。

删除tasklet:

void tasklet_kill(struct tasklet_struct *t)
{
	if (in_interrupt())
		printk("Attempt to kill tasklet from interrupt\n");

	while (test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) {
		do {
			yield();
		} while (test_bit(TASKLET_STATE_SCHED, &t->state));
	}
	tasklet_unlock_wait(t);
	clear_bit(TASKLET_STATE_SCHED, &t->state);
}

enable/disable:

static inline void tasklet_disable(struct tasklet_struct *t)
{
	tasklet_disable_nosync(t);
	tasklet_unlock_wait(t);
	smp_mb();
}

static inline void tasklet_enable(struct tasklet_struct *t)
{
	smp_mb__before_atomic_dec();
	atomic_dec(&t->count);
}

只有当enable的次数和disable的次数一致时,tasklet_schedule调度才有效。



workqueue是一个数据结构,他指导内核在稍候(未确定)或者至少一定延时后,执行用户定义的函数(没有参数)

运行在进程上下文,由内核线程evens/x(events/0代表CPU0 events/1代表CPU1...)执行调度,因此可以使用sleep

不能访问用户空间,原因是内核线程和用户空间没有内存映射。(用户调用系统函数时,内核线程代表用户进程运行,这时才有用户空间的内存映射,所以才能访问用户空间)

调度在同一CPU上

使用步骤:


1 声明work

DECLARE_WORK(n,f)

或者初始化

struct work_struct _work;

INIT_WORK(_work,_func)

2 提交work(提交到keventd_wq上)

int schedule_work(struct work_struct *work)
{
	return queue_work(keventd_wq, work);
}
也可以延时提交work

int schedule_delayed_work(struct delayed_work *dwork,unsigned long delay)
{
	return queue_delayed_work(keventd_wq, dwork, delay);
}
3 可以取消延时work

static inline int cancel_delayed_work(struct delayed_work *work)
{
	int ret;

	ret = del_timer_sync(&work->timer);
	if (ret)
		work_clear_pending(&work->work);
	return ret;
}

刷新队列(一般在cancel_delayed_work之后调用)

void flush_scheduled_work(void)
{
	flush_workqueue(keventd_wq);
}


上述工作提交到共享队列中(kevent),除了将work提交到kevent上,也可以提交到自定义的workqueue。

建立workqueue

create_workqueue(name)
create_singlethread_workqueue(name)
提交到workqueue

int queue_work(struct workqueue_struct *wq, struct work_struct *work)
{
	int ret;

	ret = queue_work_on(get_cpu(), wq, work);
	put_cpu();

	return ret;
}

延时提交到workqueue

int queue_delayed_work(struct workqueue_struct *wq,struct delayed_work *dwork, unsigned long delay)
{
	if (delay == 0)
		return queue_work(wq, &dwork->work);

	return queue_delayed_work_on(-1, wq, dwork, delay);
}

取消提交的work

void flush_workqueue(struct workqueue_struct *wq)
{
	const struct cpumask *cpu_map = wq_cpu_map(wq);
	int cpu;

	might_sleep();
	lock_map_acquire(&wq->lockdep_map);
	lock_map_release(&wq->lockdep_map);
	for_each_cpu(cpu, cpu_map)
		flush_cpu_workqueue(per_cpu_ptr(wq->cpu_wq, cpu));
}
删除workqueue

void destroy_workqueue(struct workqueue_struct *wq)
{
	const struct cpumask *cpu_map = wq_cpu_map(wq);
	int cpu;

	cpu_maps_update_begin();
	spin_lock(&workqueue_lock);
	list_del(&wq->list);
	spin_unlock(&workqueue_lock);

	for_each_cpu(cpu, cpu_map)
		cleanup_workqueue_thread(per_cpu_ptr(wq->cpu_wq, cpu));
 	cpu_maps_update_done();

	free_percpu(wq->cpu_wq);
	kfree(wq);
}




另外,有消息说,在中断服务程序上半段,同时启动work和tasklet,tasklet会优先执行




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值