[前言] 用户态的变化, 耳熟能详不在赘述. 现在支持读时共享, 写时复制.
一, 内核态的变化
1,fork 一个子进程代码
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- int main(int argc, char * argv[])
- {
- int pid;
- /* fork another process */
- pid = fork();
- if (pid <0)
- {
- /* error occurred */
- fprintf(stderr,"Fork Failed!");
- exit(-1);
- }
- else if (pid == 0)
- {
- /* child process */
- printf("This is Child Process!\n");
- }
- else
- {
- /* parent process */
- printf("This is Parent Process!\n");
- /* parent will wait for the child to complete*/
- wait(NULL);
- printf("Child Complete!\n");
- }
- }
2, 创建一个新进程在内核中的执行过程
fork,vfork 和 clone 三个系统调用都可以创建一个新进程, 而且都是通过调用 do_fork 来实现进程的创建;
3,Linux 通过复制父进程来创建一个新进程, 那么这就给我们理解这一个过程提供一个想象的框架:
(1) 复制一个 PCB--task_struct
err = arch_dup_task_struct(tsk, orig);
(2) 要给新进程分配一个新的内核堆栈
- (注意, 是内核栈, 不是用户堆栈, 用户态的是复制的)
- ti = alloc_thread_info_node(tsk, node);
- tsk->stack = ti;
- setup_thread_stack(tsk, orig); // 这里只是复制 thread_info, 而非复制内核堆栈
(3) 要修改复制过来的进程数据, 比如 pid, 进程链表等等都要改改吧, 见 copy_process 内部.
4, 从用户态的代码看 fork()---- 子进程从哪里开始执行?
函数返回了两次, 即在父子进程中各返回一次, 父进程从系统调用中返回比较容易理解, 子进程从系统调用中返回, 那它在系统调用处理过程中的哪里开始执行的呢? 这就涉及子进程的内核堆栈数据状态和 task_struct 中 thread 记录的 sp 和 ip 的一致性问题, 这是在哪里设定的? copy_thread in copy_process
- *childregs = *current_pt_regs(); // 复制内核堆栈
- childregs->ax = 0; // 为什么子进程的 fork 返回 0, 这里就是原因!
- p->thread.sp = (unsigned long) childregs; // 调度到子进程时的内核栈顶
- p->thread.ip = (unsigned long) ret_from_fork; // 调度到子进程时的第一条指令地址
来源: http://www.bubuko.com/infodetail-2806794.html