workqueue

什么是workqueue?

Linux中的Workqueue机制就是为了简化内核线程的创建。通过调用workqueue的接口就能创建内核线程。并且可以根据当前系统CPU的个数创建线程的数量,使得线程处理的事务能够并行化。



workqueue是内核中实现简单而有效的机制,他显然简化了内核daemon的创建,方便了用户的编程,



 



Workqueue机制的实现

Workqueue机制中定义了两个重要的数据结构,分析如下:



<!--[if !supportLists]-->1、         
<!--[endif]-->cpu_workqueue_struct结构。该结构将CPU和内核线程进行了绑定。在创建workqueue的过程中,Linux根据当前系统CPU的个数创建cpu_workqueue_struct。在该结构主要维护了一个任务队列,以及内核线程需要睡眠的等待队列,另外还维护了一个任务上下文,即task_struct。



<!--[if !supportLists]-->2、         
<!--[endif]-->work_struct结构是对任务的抽象。在该结构中需要维护具体的任务方法,需要处理的数据,以及任务处理的时间。该结构定义如下:



struct work_struct {



              unsigned
long pending;



               struct
list_head entry;                  /* 将任务挂载到queue的挂载点 */



               void
(*func)(void *);                   /* 任务方法 */



               void
*data;                                  /*
任务处理的数据*/



               void
*wq_data;                           /*
work的属主 */



               strut
timer_list timer;                   /* 任务延时处理定时器 */



};



      



       当用户调用workqueue的初始化接口create_workqueue或者create_singlethread_workqueue对workqueue队列进行初始化时,内核就开始为用户分配一个workqueue对象,并且将其链到一个全局的workqueue队列中。然后Linux根据当前CPU的情况,为workqueue对象分配与CPU个数相同的cpu_workqueue_struct对象,每个cpu_workqueue_struct对象都会存在一条任务队列。紧接着,Linux为每个cpu_workqueue_struct对象分配一个内核thread,即内核daemon去处理每个队列中的任务。至此,用户调用初始化接口将workqueue初始化完毕,返回workqueue的指针。



 



       在初始化workqueue过程中,内核需要初始化内核线程,注册的内核线程工作比较简单,就是不断的扫描对应cpu_workqueue_struct中的任务队列,从中获取一个有效任务,然后执行该任务。所以如果任务队列为空,那么内核daemon就在cpu_workqueue_struct中的等待队列上睡眠,直到有人唤醒daemon去处理任务队列。



 



       Workqueue初始化完毕之后,将任务运行的上下文环境构建起来了,但是具体还没有可执行的任务,所以,需要定义具体的work_struct对象。然后将work_struct加入到任务队列中,Linux会唤醒daemon去处理任务。



 



       上述描述的workqueue内核实现原理可以描述如下:
workqueue_第1张图片


    在Workqueue机制中,提供了一个系统默认的workqueue队列——keventd_wq,这个队列是Linux系统在初始化的时候就创建的。用户可以直接初始化一个work_struct对象,然后在该队列中进行调度,使用更加方便。



 



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。

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

workqueue,中文称其为工作队列,是一个用于创建内核线程的接口,通过它创建的内核线程来执行内核其他模块排列到队列里的工作,创建的内核线程被称为工作者线程。要理解工作队列的实现,重点在于理解相关的三个数据结构的含义及关系。

 
1 表示工作队列类型的数据结构:struct workqueue_struct
  1. /*
  2.  * The externally visible workqueue abstraction is an array of
  3.  * per-CPU workqueues:
  4.  */
  5. struct workqueue_struct {
  6.     struct cpu_workqueue_struct *cpu_wq;   /*工作者线程数组*/
  7.     struct list_head list; /*连接工作队列类型的链表*/
  8.     const char *name;        /*工作者线程的名称*/          
  9.     int singlethread;         /*是否创建新的工作者线程,0表示采用默认的工作者线程event/n*/
  10.     int freezeable; /* Freeze threads during suspend */
  11.     int rt;
  12. #ifdef CONFIG_LOCKDEP
  13.     struct lockdep_map lockdep_map;
  14. #endif
  15. };

内核中默认的工作队列为:

  1. static struct workqueue_struct *keventd_wq __read_mostly;

其对应的工作者线程为:event/n    其中,n代表当前cpu中processor的个数。

2. 表示工作者线程的数据结构:struct cpu_workqueue_struct

  1. /*
  2.  * The per-CPU workqueue (if single thread, we always use the first
  3.  * possible cpu).
  4.  */
  5. struct cpu_workqueue_struct {
  6.     spinlock_t lock;          /*因为工作者线程需要频繁的处理连接到其上的工作,所以需要枷锁保护*/
  7.     struct list_head worklist;
  8.     wait_queue_head_t more_work;
  9.     struct work_struct *current_work; /*当前工作线程需要处理的工作*/
  10.     struct workqueue_struct *wq;   /*该工作者线程属于那种类型的工作者队列*/
  11.     struct task_struct *thread;    /*指向工作者线程的任务结构体*/
  12. } ____cacheline_aligned;

3. 表示工作的数据结构,即工作者线程处理的对象:struct work_struct

  1. struct work_struct {
  2.     atomic_long_t data;       /*工作处理函数func的参数*/
  3. #define WORK_STRUCT_PENDING 0        /* T if work item pending execution */
  4. #define WORK_STRUCT_STATIC 1        /* static initializer (debugobjects) */
  5. #define WORK_STRUCT_FLAG_MASK (3UL)
  6. #define WORK_STRUCT_WQ_DATA_MASK (~WORK_STRUCT_FLAG_MASK)
  7.     struct list_head entry;        /*连接工作的指针*/
  8.     work_func_t func;              /*工作处理函数*/
  9. #ifdef CONFIG_LOCKDEP
  10.     struct lockdep_map lockdep_map;
  11. #endif
  12. };

再分析了以上三个对象后,重点掌握三者之间的关系。工作队列类型,工作者线程以及工作三个数据对象之间的关系如图所示。

workqueue_第2张图片

workqueue的执行非常简单,即在每次运行工作者线程的时候,去遍历工作者线程对应的工作链表上的工作,逐一进行处理即可,从这里我们也可以猜到,工作队列是没有优先级的,基本按照FIFO的方式进行处理。

你可能感兴趣的:(workqueue)