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会优先执行