您的位置:首页 > 其它

工作队列的理解

2013-08-23 09:00 155 查看
       Linux中的workqueue机制简化了内核线程的创建,通过调用workqueue的接口即可创建内核线程,而且可以根据当前系统CPU的个数创建相同数量的线程,使得多个线程能够并行处理事件。
  工作队列允许内核函数(例如可延迟函数)激活,稍后由一种叫做工作者线程的内核线程来执行,它和可延迟函数有一定的区别:
不同点:
  可延迟函数运行于中断上下文(不可睡眠),不一定在创建它的进程中运行。
  工作队列中的函数运行于进程上下文(可睡眠),由内核线程来执行。
相同点:
  因为可延迟函数运行时,不可能有正在运行的进程,而工作队列是由内核线程执行的,所以它们都不能访问用户地址空间。

工作队列的使用:
内核中通过下述结构体表示一个具体的工作(任务):

struct work_struct
{
unsigned long pending;//这个工作是否正在等待处理
struct list_head entry;//链接所有工作的链表,形成工作队列
void (*func)(void *);//处理函数
void *data;//传递给处理函数的参数
void *wq_data;//内部使用数据
struct timer_list timer;//延迟的工作队列所用到的定时器
};
这些工作(结构体)链接成的链表就是工作队列。工作者线程会在被唤醒时,执行链表上的所有工作,当一个工作被执行完后,相应的work_struct结构体会被删除。当这个工作链表上没有工作时,工作线程就会睡眠。

操作步骤:
1、创建
  创建分为工作队列的创建和工作函数(任务)的创建.
  (1)工作队列的创建需要有其描述符,它的数据结构是 workqueue_struct. 该结构定义在<linux/workqueue.h>中.这里我们不需要关心它的具体组成,内核已经写好了两个函数用于创建队列:
      struct workqueue_struct *create_workqueue(const char *name);

      struct workqueue_struct * workqueue_singlethread_workqueue(const char *name);
         它们的区别在于实际处理器的多少,如果是单核处理器的话,他们毫无区别.
   因为每个工作队列都有一个或多个(多核处理器)专用的进程(内核线程),这些进程运行提交到该工作队列函数. create_workqueue内核会在系统中的每个处理器上为该工作队列创建专用的线程. 这样,如果工作队列足够多的话,可能对系统的性能有所影响,而create_singlethread_workqueue则只会创建一个专用的线程.
所以,如果单个工作线程足够使用,推荐使用第二个函数来创建工作队列.
   同样的,工作任务的创建也需要有其描述符,它的数据结构是work_struct.内核同样创建了几个宏用于创建工作任务:
   DECLARE_WORK(name,void (*function)(void *),void *data);//用于在内核编译时使用.

   INIT_WORK(struct work_struct *work,void(*function)(void *),void *data);//用于在系统运行时创建.首次创建时使用它.

   PREPARE_WORK(struct work_struct *work,void(*function)(void *),void *data);//用于在系统运行时创建.没有INIT_WORK初始化彻底,因为它不会初始化用来将work_struct结构连接到工作队列的指针.如果结构已经被提交到工作队列,而只是需要修改该结构,则应该使用PREPARE_WORK而不是INIT_WORK.
2、添加任务:
   将任务添加到自定义的工作队列,可使用如下两个函数之一:
   int queue_work(struct workqueue_struct *queue,struct work_struct *work);

   int queue_delayed_work(struct workqueue_struct *queue,struct work_struct *work,unsigned long delay);
   它们都会将work添加到自定义的queue.但是如果使用queue_delayed_work,则实际工作至少会在经过指定的jiffies(由delay指定)之后才会执行.如果工作被成功添加到队列,则上述函数的返回值为1,返回值为非零表示给定的work_struct结构已经等待在该队列中,不能再次加入该队列.
将任务添加到内核全局的工作队列,则可使用如下两个函数之一:
int schedule_work(struct work_struct *work);

int schedule_delayed_work(struct delayed_work *dwork, unsigned long delay);
3、删除:
    结束对工作队列的使用后,可调用下面的函数释放相关资源:
  void destroy_workqueue(struct workqueue_struct *queue);
      如果想继续使用该工作队列,可刷新该工作队列(清理其中的所有任务):
void flush_workqueue(struct workqueue_struct *wq);//清理指定工作队列中的所有任务

void flush_scheduled_work(void);//清理内核全局工作队列中的所有任务
注意:以上函数的调用者都会阻塞直到操作完成为止,如果任务正在被执行,则会等待其执行完。

Workqueue编程接口:

序号
接口函数
说明
1
create_workqueue

用于创建一个workqueue队列,为系统中的每个CPU都创建一个内核线程。输入参数:

@name:workqueue的名称

2
create_singlethread_workqueue

用于创建workqueue,只创建一个内核线程。输入参数:

@name:workqueue名称

3
destroy_workqueue

释放workqueue队列。输入参数:

@ workqueue_struct:需要释放的workqueue队列指针

4
schedule_work

调度执行一个具体的任务,执行的任务将会被挂入Linux系统提供的workqueue——keventd_wq输入参数:

@ work_struct:具体任务对象指针

5
schedule_delayed_work

延迟一定时间去执行一个具体的任务,功能与schedule_work类似,多了一个延迟时间,输入参数:

@work_struct:具体任务对象指针

@delay:延迟时间

6
queue_work

调度执行一个指定workqueue中的任务。输入参数:

@ workqueue_struct:指定的workqueue指针

@work_struct:具体任务对象指针

7
queue_delayed_work

延迟调度执行一个指定workqueue中的任务,功能与queue_work类似,输入参数多了一个delay。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  内核 工作队列