Linux工作队列workqueue实现分析

本文档的Copyleft归rosetta所有,使用GPL发布,可以自由拷贝、转载,转载时请保持文档的完整性。
参考资料:《Linux内核设计与实现》第3版 LKD3e、linux-2.6.27

工作队列子系统是一个用于调用创建内核线程的接口,通过它创建的线程负责执行由内核其它部分排到队列里的任务。这些内核线程称为工作者线程。工作队列子系统提供了一个缺省的工作都线程来处理工作。一般使用缺省线程即可,但当处理密集型和性能要求严格的任务时,创建拥有自己的工作者线程比较好。(引至LKD3e)

这个接口就是create_workqueue(),它返回一个struct workqueue_struct 结构指针。
        /*
         * The externally visible workqueue abstraction is an array of
         * per-CPU workqueues:
         */
        struct workqueue_struct {
            struct cpu_workqueue_struct *cpu_wq;
            struct list_head list;
            const char *name;
            int singlethread;
            int freezeable;     /* Freeze threads during suspend */
        #ifdef CONFIG_LOCKDEP
            struct lockdep_map lockdep_map;
        #endif
        };

上面注释:外部可见的工作队列抽象是由per-CPU的工作队列组成的数组,这个数组为结构体 cpu_workqueue_struct 。所以每种任务,它有一个自己的工作队列(struct workqueue_struct ),这个工作队列如果需要的话会为每个CPU创建对应的工作者线程。也就是说每个CPU,每个工作者线程对应一个cpu_workqueue_struct。

/*
         * The per-CPU workqueue (if single thread, we always use the first
         * possible cpu).
         */
        struct cpu_workqueue_struct {
       
            spinlock_t lock;
       
            struct list_head worklist;
            wait_queue_head_t more_work;
            struct work_struct *current_work;
       
            struct workqueue_struct *wq;
            struct task_struct *thread;
       
            int run_depth;      /* Detect run_workqueue() recursion depth */
        } ____cacheline_aligned;

刚才说了,每种任务,它都有一个自己的工作队列,这种任务的抽象就是为每个CPU创建一个处理这种任务的工作者线程(当然这是需要的情况下,如果不需要则会使用默认的工作者线程events/n,n为CPU编号),那么这个wq就是关联到自己的工作队列workqueue_struct。所有的工作者线程都是用普通的内核线程实现的,由worker_thread()函数完成。
当为一个CPU创建完一个线程后,这个线程执行死循环开始休眠,当有操作插入到队列时,线程被唤醒并执行。(LKD3e)

对应的具体工作由work_struct结构:
        struct work_struct {
            atomic_long_t data;
            struct list_head entry;
            work_func_t func;
        };
        由list_head可知,它是个双向链表,每个结点为一个work_struct结构类型。每个CPU上的每种类型的队列都对应这样一个链表。当一个工作线程被唤醒时,它会执行这个链表上的所有工作;工作执行完毕后就从链表上先移除相应的work_struct;当链表上不再有对象时就继续休眠。
        总得来说就是每种任务(可以理解成为处理不同数据结构),有一个workqueue_struct。每个CPU有多个工作者线程,
        每个线程处理相应的任务。处理过程最终调用的是func。至于func是怎么赋值的可参考下面代码实现。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/pssxs.html