工作队列

参考:1、http://blog.csdn.net/droidphone/article/details/7518428

          2、http://blog.csdn.net/lizhiguo0532/article/details/6533443

          3、《内核设计与实现》

          4、2.6.34

首先,看下默认工作队列keventd_wq和管理调度其它内核线程(当然也包含工作线程)时需要的kthreadd线程的创建过程;

start_kernel()---->rest_init()
                  |---->kernel_thread(kernel_init, NULL, CLONE_FS | 
                  |                   CLONE_SIGHAND);
| |---->pid = kernel_thread(kthreadd, NULL, CLONE_FS |
                  |                   CLONE_FILES);
|----kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns); | kthreadd_task保存线程kthreadd的task_struct结构体地址 | kthreadd线程根据kthread_create_list上链入的kthread_create_info结构体来创建特定线程.


kthreadd的工作流程,从kthread_create_list上取下信息,来创建新的内核线程。
kthreadd()---->struct kthread_create_info *create;
         |     create = list_entry(kthread_create_list.next,
         |                         struct kthread_create_info, list);
         |----create_kthread(create);
              |----pid = kernel_thread(kthread, create, CLONE_FS | 
              |                        CLONE_FILES | SIGCHLD);


static int kthread(void *_create)
    |----int (*threadfn)(void *data) = create->threadfn;
    |----void *data = create->data;
    |----create->result = current; //kthread_create_info的result保存新创建内核线程的task_struct结构体指针
    |----complete(&create->done);
    |----ret = threadfn(data); //根据kthread_create_info的信息执行相应的函数
    |----do_exit(ret);

kernel_init()---->do_basic_setup()
                  |---->init_workqueues()

init_workqueues()---->keventd_wq = create_workqueue("events"); //创建kevent_wq工作队列

如何创建kevent_wq工作队列,如下:

create_workqueue("events")
    |---- __create_workqueue_key("event", 0, 0, 0, NULL, NUL); //非signle,每个核上都创建


__create_workqueue_key("event", 0, 0, 0, NULL, NULL)
   |---->struct workqueue_struct *wq;
   |     struct cpu_workqueue_struct *cwq;
   |     wq = kzalloc(sizeof(*wq), GFP_KERNEL);
   |     wq->cpu_wq = alloc_percpu(struct cpu_workqueue_struct);
   |
   |----list_add(&wq->list, &workqueues);所有的workqueue都链入workqueues
   |
   |对于该类型的工作队列
   |每个核上都建立一个对应的工作线程
   |for_each_possible_cpu(cpu)
   |---->cwq = init_cpu_workqueue(wq, cpu);
   |---->err = create_workqueue_thread(cwq, cpu);


在每个核上如何创建工作线程,如下:
int create_workqueue_thread(struct cpu_workqueue_struct *cwq, int cpu)
   |---->struct task_struct *p;
   |     p = kthread_create(worker_thread, cwq, fmt, wq->name, cpu);
         |---->struct kthread_create_info create;
         |     create.threadfn = threadfn;
         |     create.data = data;
         |---->init_completion(&create.done); //初始化完成量
         |
         |---->list_add_tail(&create.list, &kthread_create_list);
         |         将新建的create链接到全局的线程链表kthread_create_list|
         |---->wake_up_process(kthreadd_task); //唤起threadd线程来创建新的内核线程
         |    kthread_task保存内核线程kthreadd的task_struct地址
         |    直接效果就是worker_thread(cwq);
         |
         |---->wait_for_completion(&create.done);
         |.......
  |cwq->thread = p;

对于每一种类型的工作队列,除了signlethread情形外,在各个核上都创建了相应类型的工作线程。无论时哪种类型的工作队列,各个工作线程最终都会调用worke_thread线程函数。

static int worker_thread(void* __cwq)
    |----DEFINE_WAIT(wait);
    |
    |循环forever
    |---->prepare_to_wait(&cwq->more_work, &wait, TASK_INTERRUPTIBLE);
    |
    |         if (!freezing(current) &&
    |             !kthread_should_stop() &&
    |             list_empty(&cwq->worklist))
    |                   schedule();//队列上没有工作,睡眠
    |
    |       finish_wait(&cwq->more_work, &wait);
    |        
    |       try_to_freeze();
    |       
    |       if (kthread_should_stop())
    |              break;
    |       
    |       run_workqueue(cwq);//执行对列上的工作
    |.....never_exit

run_workqueue(cwq)
    |----struct work_struct *work = 
    |    list_entry(cwq->worklist.next, struct work_struct, entry)
    |----work_func_t f = work->func;
    |----cwq->current_work = work;
    |    list_del_init(cwq->worklist.next);
    |    work_clear_pending(work);
    |    f(work); //执行相应的工作函数
    |----cwq->current_work = NULL;                                                                                

我们看到,在worker_thread中,工作线程将睡眠,那么何时将其唤醒,构建完工作后,将schedule_work,此函数中将唤醒默认的工作线程。

将工作提交给默认队列时所进行的业务:
int schedule_work(struct work_struct *work)
    |---->queue_work(keventd_wq, work); 
    |     keventd_wq工作队列是在函数init_workqueues()中创建的


queue_work()
int queue_work(struct workqueue_struct *wq, struct work_struct *work)
    |---->queue_work_on(get_cpu(), wq, work);
         |---->__queue_work(wq_per_cpu(wq, cpu), work);


__queue_work()
static void __queue_work(struct cpu_workqueue_struct *cwq,
                         struct work_struct *work)
    |----insert_work(cwq, work, &cwq->worklist);
         |---->set_wq_data(work, cwq); //注意work_struct结构体中data域的用法
               |----new = (unsigned long) cwq | 
               |           (1UL << WORK_STRUCT_PENDING);
               |----new |= WORK_STRUCT_FLAG_MASK & 
               |           *work_data_bits(work);
               |----atomic_long_set(&work->data, new);
         |---->list_add_tail(&work->entry, head);
         |     将工作项加入等待队列
         |---->wake_up(&cwq->more_work);
         |     唤醒等待在该等待队列头上的所有等待队列项



至于创建新的工作队列(相应的创建工作线程),由于以上流程从kevetd_wq工作队列的创建开始分析,因此很容易。

原文地址:https://www.cnblogs.com/openix/p/3295628.html