进程创建
需要注意 linux 的所有进程(除了 pid 为 1 的 init 进程)都是在父进程的基础上创建的,类似于 Java 中的所有类都是 Object 的子类,我们可以使用 ps -el
列出系统中的所有当前活动进程的完整信息。通过递归跟踪父进程一直到进程 init,形象地看,是一棵进程的树:

当进程创建新进程时,可有两种执行可能:
- 父进程与子进程并发执行。
- 父进程等待,直到某个或全部子进程执行完。
新进程的地址空间也有两种可能:
- 子进程是父进程的复制品(它具有与父进程同样的程序和数据)。
- 子进程加载另一个新程序。
linux 的代码通过 fork() 命令将当前运行的程序翻倍,执行处于系统调用 fork() 之后的指令,但有一点不同:对于新(子)进程,系统调用 fork() 的返回值为 0;而对于父进程,返回值为子进程的进程标识符(非零)。
通常,在系统调用 fork() 之后,有个进程(一般是使用 fork() 返回值判断使用新进程,新进程使用 exec() )使用系统调用 exec(),以用新程序来取代进程的内存空间。系统调用 exec() 加载二进制文件到内存中(破坏了包含系统调用 exec() 的原来程序的内存内容),并开始执行。采用这种方式,这两个进程能相互通信,并能按各自方法运行。
进程终止
当进程完成执行最后语句并且通过系统调用 exit() 请求操作系统删除自身时,进程终止。这时,进程可以返回状态值(通常为整数)到父进程(通过系统调用 wait())。所有进程资源,如物理和虚拟内存、打开文件和 I/O 缓冲区等,会由操作系统释放。
当一个进程终止时,操作系统会释放其资源。不过,它位于进程表中的条目还是在的,直到它的父进程调用 wait();这是因为进程表包含了进程的退出状态。当进程已经终止,但是其父进程尚未调用 wait(),这样的进程称为僵尸进程。
3 个 x_fork()
Linux 提供了 fork、vfork、clone 三个进程创建方法。 这三个调用的执行过程是执行 fork(),vfork(),clone() 时,通过一个系统调用表映射到sys_fork(),sys_vfork(),sys_clone(),再在这三个函数中去调用 do_fork() 去做具体的创建进程工作。
fork()
fork() 创建一个进程时,子进程只是完全复制父进程的资源,复制出来的子进程有自己的 task_struct 结构和 pid,但却复制父进程其它所有的资源。
通过fork创建子进程,需要将上面描述的每种资源都复制一个副本(写时复制)。
v_fork()
vfork系统调用不同于fork,用vfork创建的子进程与父进程共享地址空间,也就是说子进程完全运行在父进程的地址空间上,如果这时子进程修改了某个变量,这将影响到父进程。
用 vfork() 创建的子进程必须显示调用 exit() 来结束,否则子进程将不能结束,而fork()则不存在这个情况。
用 vfork() 创建子进程后,父进程会被阻塞直到子进程调用 exec()(将一个新的可执行文件载入到地址空间并执行之)或 exit()。
clone()
系统调用 fork() 和 vfork() 是无参数的,而 clone() 则带有参数。fork() 是全部复制,vfork() 是共享内存,而 clone() 是则可以将父进程资源有选择地复制给子进程,而没有复制的数据结构则通过指针的复制让子进程共享,具体要复制哪些资源给子进程,由参数列表中的 clone_flags 来决定。另外,clone() 返回的是子进程的pid。