工作队列的使用又分两种情况,一种是利用系统共享的工作队列来增加自己的工作,这种情况处理函数不能消耗过多时间,这样会影响共享队列中其他任务的处理;另一种是创建自己的工作队列并添加工作。
(一)利用系统共享的工作队列添加工作:
第一步:声明或编写一个工作处理函数void my_func();
第二步:创建一个工作结构体变量,并将处理函数和参数的入口地址赋给这个工作结构体变量
DECLARE_WORK(my_work,my_func,&data); //编译时创建名为my_work的结构体变量并把函数入口地址my_func和参数地址&data赋给它;
假如不想要在编译时创建,就用DECLARE_WORK()创建并初始化工作结构体变量,
也可以在程序运行时初始化一个工作队列可用INIT_WORK()创建
struct work_struct my_work; //创建一个名为my_work的结构体变量,创建后才能使用INIT_WORK()
INIT_WORK(&my_work,my_func,&data); //初始化已经创建的my_work,其实便是往这个结构体变量中添加处理函数的入口地址和data的地址,通常在驱动的open函数中完成
第三步:将工作结构体变量添加入系统的共享工作队列
schedule_work(&my_work); //添加入队列的工作完成后会自动从队列中删除
或schedule_delayed_work(&my_work,tick); //延时tick个滴答后再提交工作
(二)创建自己的工作队列来添加工作
第一步:声明工作处理函数和一个指向工作队列的指针
void my_func();
struct workqueue_struct *p_queue;
第二步:创建自己的工作队列和工作结构体变量(通常在open函数中完成)
创建, 使用一个下列的 2 个函数:
struct workqueue_struct *create_workqueue(const char *name); struct workqueue_struct *create_singlethread_workqueue(const char *name);
每个工作队列有一个或多个专用的进程("内核线程"), 它运行提交给这个队列的函数. 如果你使用 create_workqueue, 你得到一个工作队列它有一个专用的线程在系统的每个处理器上. 在很多情况下, 所有这些线程是简单的过度行为; 如果一个单个工作者线程就足够, 使用 create_singlethread_workqueue 来代替创建工作队列
eg:
===========================================================================================================
工作队列 API 比微线程稍复杂,主要是因为它支持很多选项。 我们首先探讨一下工作队列,然后再看一下任务和变体。
通过 图 3 可以回想工作队列的核心结构体是队列本身。 该结构体用于将任务安排出 top half ,进入 bottom half ,从而延迟它的执行。 工作队列通过宏调用生成 create_workqueue
,返回一个 workqueue_struct
参考值。 可以通过调用函数 destroy_workqueue
来远程遥控工作队列(如果需要):
struct workqueue_struct *create_workqueue( name );
void destroy_workqueue( struct workqueue_struct * );
|
通过工作队列与之通信的任务可以由结构体 work_struct
来定义。 通常,该结构体是用来进行任务定义的结构体的第一个元素(后面有相关例子)。 工作队列 API 提供三个函数来初始化任务(通过一个事先分配的缓存); 参见 清单 6。 宏 INIT_WORK
提供必需的初始化数据以及处理程序函数的配置(由用户传递进来)。 如果开发人员需要在任务被排入工作队列之前发生延迟,可以使宏INIT_DELAYED_WORK
和 INIT_DELAYED_WORK_DEFERRABLE
。
清单6 任务初始化 INIT_WORK( work, func );
INIT_DELAYED_WORK( work, func );
INIT_DELAYED_WORK_DEFERRABLE( work, func );
|
任务结构体的初始化完成后,接下来要将任务安排进工作队列。 可采用多种方法来完成这一操作(参见 清单 7)。 首先,利用queue_work
简单地将任务安排进工作队列(这将任务绑定到当前的 CPU)。 或者,可以通过 queue_work_on
来指定处理程序在哪个 CPU 上运行。 两个附加的函数为延迟任务提供相同的功能(其结构体装入结构体 work_struct
之中,并有一个 计时器用于任务延迟 )。
清单7 将任务安排进工作队列
int queue_work( struct workqueue_struct *wq, struct work_struct *work );
int queue_work_on( int cpu, struct workqueue_struct *wq, struct work_struct *work );
int queue_delayed_work( struct workqueue_struct *wq,struct delayed_work *dwork, unsigned long delay );
int queue_delayed_work_on( int cpu, struct workqueue_struct *wq, struct delayed_work *dwork, unsigned long delay );
|
也可以使用全局的内核全局工作队列,利用 4 个函数来为工作队列定位。 这些函数(见 清单 8)模拟 清单 7,只是不需要定义工作队列结构体。
int schedule_work( struct work_struct *work );
int schedule_work_on( int cpu, struct work_struct *work );
int scheduled_delayed_work( struct delayed_work *dwork, unsigned long delay );
int scheduled_delayed_work_on( int cpu, struct delayed_work *dwork, unsigned long delay );
|
还有一些帮助函数用于清理或取消工作队列中的任务。想清理特定的任务项目并阻塞任务, 直到任务完成为止, 可以调用flush_work
来实现。 指定工作队列中的所有任务能够通过调用 flush_workqueue
来完成。 这两种情形下,调用者阻塞直到操作完成为止。 为了清理内核全局工作队列,可调用 flush_scheduled_work
。
int 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
。
int cancel_work_sync( struct work_struct *work );
int cancel_delayed_work_sync( struct delayed_work *dwork );
|
最后,可以通过调用 work_pending
或者 delayed_work_pending
来确定任务项目是否在进行中。
work_pending( work );
delayed_work_pending( work ); |