==========================================================================
刘旸 + 原创作品转载请注明出处
《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
==========================================================================
理论基础:
进程描述符(task_struct)
用来描述进程的数据结构,可以理解为进程的属性。比如进程的状态、进程的标识(PID)等,都被封装在了进程描述符这个数据结构中,该数据结构被定义为task_struct
进程控制块(PCB)
是操作系统核心中一种数据结构,主要表示进程状态。
进程状态
创建一个进程至少涉及的函数:
sys_clone,do_fork,dup_task_struct,
copy_process,copy_thread,ret_from_fork
下面我们通过跟踪fork()来观察Linux是如何创建新进程。
1.将fork()添加到MenuOS。
2.编写fork()的具体实现代码。
3.编译并启动MenuOS。
4.使用gdb调试MenuOS,在sys_fork()处设置断点,continue。
5.在MenuOS中键入命令fork,使其在sys_fork()处停下。
6.用list命令查看代码,s命令追踪语句执行状况。
分析与总结:
1.不论是使用fork()/vfork()还是clone()来创建进程,最终都是通过do_fork()方法来实现的。
2.do_fork()流程:
1)首先调用copy_process()为子进程复制出一份进程信息,如果是vfork()则初始化完成处理信息;
2)然后调用wake_up_new_task将子进程加入调度器,为之分配CPU,如果是vfork(),则父进程等待子进程完成exec替换自己的地址空间。
3.copy_process()流程:
1)首先调用dup_task_struct()复制当前的task_struct,检查进程数是否超过限制;
2)接着初始化自旋锁、挂起信号、CPU 定时器等;
3)然后调用sched_fork初始化进程数据结构,并把进程状态设置为TASK_RUNNING,复制所有进程信息,包括文件系统、信号处理函数、信号、内存管理等;
4)调用copy_thread()初始化子进程内核栈,为新进程分配并设置新的pid。
4.dup_task_struct()流程:
1)调用alloc_task_struct_node()分配一个 task_struct 节点;
2)调用alloc_thread_info_node()分配一个 thread_info 节点,其实是分配了一个thread_union联合体,将栈底返回给 ti;
3)最后将栈底的值 ti 赋值给新节点的栈。
5.sched_fork():sched_fork()大致完成了两项重要工作,一是将子进程状态设置为 TASK_RUNNING,二是为其分配 CPU。
6.copy_thread():这部分为我们解释了两个及其重要的问题:
1)为什么fork()在子进程中返回0,原因是childregs->ax = 0;这段代码将子进程的 eax 赋值为0。
2)p->thread.ip = (unsigned long) ret_from_fork;将子进程的ip设置为ret_form_fork的首地址,因此子进程是从ret_from_fork开始执行的。
最后附上fork流程图: