- 论坛徽章:
- 0
|
本帖最后由 MagicBoy2010 于 2010-07-20 14:40 编辑
这楼的帖子用来扩展1楼帖子中提到的第一部分>>
-------------------------------------------------------------------------
关于worker_thread内核线程的创立过程:
最开始由void __init init_workqueues(void)发起(Workqueue.c),依次的关键调用节点分别是(为了便于叙述,在内核代码基础上略有改动,但不影响核心调用过程)
create_workqueue("events");
__create_workqueue((("events"), 0, 0);
__create_workqueue_key("events", 0, 0, NULL, NULL);
在__create_workqueue_key("events", 0, 0, NULL, NULL)中:
1).首先创建一个struct workqueue_struct指针型变量*wq, wq->name = "events".这个新建的队列指针将被记录在全局变量keventd_wq中.
workqueue_struct定义如下:- 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
- };
复制代码 2).把wq所在的队列节点加入到一名为workqueues的全局变量中(list_add(&wq->list, &workqueues)).
3).调用create_workqueue_thread(),最终调用kthread_create(worker_thread, cwq, fmt, wq->name, cpu)来生成内核线程worker_thread.
Linux内核中最终是通过do_fork来生成内核线程(正如前面所说,这其实是个能被调度的进程,拥有自己的task_struct结构),这个过程在内核中是个比较复杂的过程,比较重要的节点总结如下:在当前进程下调用do_fork来生成一个新进程时,会大量copy当前进程的task_struct结构到新进程的task_struct变量中,新进程如果被调度运行,入口点(pc值)是kernel_thread_helper函数,在该函数中会再次将pc值设置为kernel_thread()中的function指针,也就是在调用kernel_thread函数时第一参数所表示的函数。
所以,在Linux系统初始化期间,会生成一个新进程,该进程的执行线程/函数为worker_thread,该进程被创建出来之后的状态是STOP,这意味着该线程无法进入调度队列直到针对该进程调用wake_up_process(),该进程才会真正进入调度队列,如果被调度,则开始运行worker_thread函数。新进程被赋予的调度优先级为KTHREAD_NICE_LEVEL(-5),这个标志的实际含义将在Linux进程调度的帖子里去写。然后对于worker_thread线程函数本身,会进一步调整调度优先级(set_user_nice(current, -5)),这样worker_thread所在进程的优先值将为-10,在可抢占式的Linux内核中,如此高的调度优先级极易导致一个调度时点,即使当前进程也许正运行在内核态,也可能被切换出CPU,代之以worker_thread.
该过程的理解也可以参考海豚的另一个帖子:解密Linux kernel中的内核线程 |
|