工作队列

参考: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工作队列的创建开始分析,因此很容易。

你可能感兴趣的:(工作)