一、描述
工作队列(work queue) 类似 tasklet,允许调用者请求在将来某个时间调用一个函数。
tasklet 在软件中断上下文中运行,所以tasklet执行很快,持续短,并且一般在原子态。
tasklet一般只能在最初被提交的处理器上运行。
工作队列在一个特殊内核进程上下文运行,有更多的灵活性,并且能够休眠。工作队列包括一系列将要执行的任务和执行这些任务的内核线程。每个工作队列有一个专门的线程,所有的任务必须在进程的上下文中运行,这样它们可以安全休眠。
二、代码数据结构
1. tasklet
struct tasklet_struct
{
struct tasklet_struct *next;
unsigned long state;
atomic_t count;
void (*func)(unsigned long);//tasklet的处理函数 ---- 底半部
unsigned long data;//传递给tasklet处理函数的参数
};
2. 编程使用
1.分配初始化
struct tasklet_struct mytasklet;
tasklet_init(&mytasklet,处理函数,处理函数的参数);
或者
DECLARE_TASKLET(tasklet名,处理函数,处理函数的参数);
2.在顶半部中登记tasklet
tasklet_schedule(&mytasklet);
tasklet本身运行在中断上下文,处理函数不能 阻塞/睡眠。
3.工作队列
tasklet处理函数不允许睡眠,如果底半部需要睡眠可以选择工作队列,工作队列包括工作和延时工作。
//工作
typedef void (*work_func_t)(struct work_struct *work);//函数指针类型
struct work_struct {
atomic_long_t data;
struct list_head entry;
work_func_t func;//工作的处理函数
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
//延时工作 -------- 指定延时多久后才处理
struct delayed_work {
struct work_struct work;//工作
struct timer_list timer;//内核定时器
};
4. 编程使用
1)分配初始化
struct work_struct mywork;
struct delayed_work mydwork;
INIT_WORK(&mywork,工作处理函数);
INIT_DELAYED_WORK(&mydwork,延时工作处理函数);
2)在顶半部中登记工作/延时工作
schedule_work(&mywork);//空闲时调用
schedule_delayed_work(&mydwork,延时时间);//延时指定时间调用 HZ <===> 1s
(3)注意事项
工作/延时工作 工作于进程上下文,使用内核线程来执行,参与任务调度,可以睡眠
三、比较
比较点 | Tasklet | 工作队列 |
---|---|---|
执行上下文 | 延后的工作(中断底半部),运行于中断上下文 | 延后的工作(中断底半部),运行于进程上下文 |
可重用 | 不能在不同的CPU上同时运行,但是不同的CPU可以运行不同的 tasklet | 可以在不同的CPU上同时运行 |
睡眠 | 不能睡眠 | 可以睡眠 |
抢占 | 不能抢占/调度 | 可以抢占/调度 |
易用性 | 容易使用 | 容易使用 |
使用场合 | 如果延后的工作不会睡眠 | 如果延后的工作会睡眠 |