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