Linux设备驱动之workqueue
工作队列是一种将工作推后执行的形式,交由一个内核线程去执行在进程上下文执行,其不能访问用户空间。最重要特点的就是工作队列允许重新调度甚至是睡眠。
在内核代码中, 经常希望延缓部分工作到将来某个时间执行, 这样做的原因很多, 比如
- 在持有锁时做大量(或者说费时的)工作不合适。
- 希望将工作聚集以获取批处理的性能。
- 调用了一个可能导致睡眠的函数使得在此时执行新调度非常不合适。 ... 内核中提供了许多机制来提供延迟执行, 使用最多则是 workqueue。
工作队列(workqueue)是另外一种将工作推后执行的形式.工作队列可以把工作推后,交由一个内核线程去执行,也就是说,这个下半部分可以在进程上下文中执行。最重要的就是工作队列允许被重新调度甚至是睡眠。
对于使用者,基本上只需要做 3 件事情,依次为:
- 创建工作队列 ( 如果使用内核默认的工作队列,连这一步都可以省略掉 )
- 创建工作项
- 向工作队列中提交工作项
执行在进程上下文中,这样使得它可以睡眠,被调度及被抢占,在多核环境下的使用也非常友好。
数据结构
工作: 所谓work就是异步执行的函数。用数据结构
struct work_struct
表示。工作队列:
struct workqueue_struct
如果是多线程,Linux根据当前系统CPU的个数创建
struct cpu_workqueue_struct
:
包含的头文件为 <linux/workqueue.h>
创建步骤
静态地创建work工作同时初始化:
静态地创建一个名为n,待执行函数为f,函数的参数为data的work_struct结构。
1 |
|
一般而言,work都是推迟到worker thread 被调度的时刻,但是有时候,我们希望在指定的时间过去之后再调度worker thread 来处理该work,这种类型的work被称作delayed work,DECLARE_DELAYED_WORK用来初始化delayed work,它的概念和普通work类似。
动态地创建work工作,随后初始化:
先创建工作任务,后绑定处理函数 1
struct work_struct work;
INIT_WORK
。
1 | INIT_WORK(struct work_struct *work, work_func_t func); |
清除或取消工作队列中的work工作
想清理特定的任务项目并阻塞任务,直到任务完成为止,可以调用
flush_work()
来实现。
指定工作队列中的所有任务能够通过调用 flush_workqueue
来完成。 这两种情形下,调用者阻塞直到操作完成为止。
为了清理内核全局工作队列,可调用
flush_scheduled_work()
。
1
2
3int flush_work( struct work_struct *work );
int flush_workqueue( struct workqueue_struct *wq );
void flush_scheduled_work( void );cancel_work_sync()
将会终止队列中的任务或者阻塞任务直到回调结束(如果处理程序已经在处理该任务)。
如果任务被延迟,可以调用 cancel_delayed_work_sync()
。
1 | int cancel_work_sync( struct work_struct *work ); |
最后,可以通过调用 work_pending()
或者
delayed_work_pending()
来确定任务项目是否在进行中。
1 | /** |
创建销毁workqueue
- 用于创建一个workqueue队列,为系统中的每个CPU都创建一个内核线程。
1
struct workqueue_struct *create_workqueue(const char *name);
- 用于创建workqueue,只创建一个内核线程。
1
struct workqueue_struct *create_singlethread_workqueue(const char *name);
- 释放workqueue队列。
1
void destroy_workqueue(struct workqueue_struct *queue);
使用内核提供的共享列队
系统中包括若干的workqueue,最著名的workqueue就是系统缺省的的工作队列
keventd_wq
了,定义如下: 1
static struct workqueue_struct *keventd_wq __read_mostly;
1
int schedule_work(struct work_struct *work);
1
void flush_scheduled_work(void);
1
int schedule_delayed_work(struct delayed_struct *work, unsigned long delay);
1
int cancel_delayed_work(struct delayed_struct *work);
使用自定义队列
将工作加入工作列队进行调度
1
int queue_work(struct workqueue_struct *wq, struct work_struct *work)
释放创建的工作列队资源
1
void destroy_workqueue(struct workqueue_struct *wq)
将工作加入指定延时工作列队
1
2
3
4
5
6
7/*
* queue_delayed_work - queue work on a workqueue after delay
* @wq: workqueue to use
* @dwork: delayable work to queue
* @delay: number of jiffies to wait before queueing
*/
bool queue_delayed_work(struct workqueue_struct *wq, struct delay_struct *work, unsigned long delay)取消指定工作列队的延时工作
1
bool cancel_delayed_work(struct delay_struct *work)
等待列队中的任务全部执行完毕。
1
void flush_workqueue(struct workqueue_struct *wq);
样例代码
1 | /* https://github.com/cirosantilli/linux-kernel-module-cheat#workqueues */ |