Linux 进程会形成父子关系。Shell 通常先创建子进程,再安排该子进程执行所请求的程序,从而启动外部命令。经典解释会把这项工作分成 fork 和 exec 操作。
进程 · 第 4 课
进程创建
学习 fork、exec、PID 和父进程关系如何参与 Linux 进程创建。
使用 `fork` 创建子进程
fork() 系统调用会根据调用进程创建子进程。父进程和子进程都会从 fork 的返回点继续,但获得不同的返回值和不同的 PID。
子进程获得逻辑上独立的进程状态。Linux 最初可以使用写时复制共享物理内存页面,只有某个进程修改页面时才复制。打开的文件描述符会被继承,并引用相同的底层打开文件描述,因此文件偏移等细节可能继续共享。
成功的 fork() 会创建什么?
使用 `execve` 替换程序
execve() 调用会把新程序加载到调用进程中。成功时,它会替换进程映像,不会返回旧程序。PID 保持不变,因为 execve() 不会创建新进程。
因此,许多 shell 命令遵循 fork-exec 模式:
- Shell 创建子进程。
- 子进程准备重定向和其他执行状态。
- 子进程执行所请求的程序。
- Shell 根据前台或后台执行选择等待或继续。
库和应用程序可以提供 posix_spawn() 等更高层接口,Linux 也有 clone() 等其他原语。熟悉的 fork-exec 模型仍然有用,但不是唯一可能的接口。
成功执行 execve() 后,进程的 PID 会发生什么?
检查父进程和子进程 ID
PID 标识进程,PPID 标识其父进程。明确请求这些字段:
$ ps -o pid,ppid,stat,cmd
如果 shell 启动 ps,该 shell 的 PID 通常会显示为 ps 进程的 PPID。时机很重要:短生命周期进程可能在单独的观察命令捕获之前就已经退出。
进程列表中的 PPID 表示什么?
PID 1 和重新指定父进程
内核会以 PID 1 启动第一个用户空间进程。根据系统,它可能是 systemd、其他 init 实现,或容器/PID 命名空间中的小型 init。PID 1 会启动并监督部分用户空间环境,还承担特殊的信号和孤儿进程回收职责。
父进程先于子进程退出时,子进程会被重新指定给适当的 subreaper,或其 PID 命名空间中的 init 进程。它不必仅因原父进程结束而终止。
关于 PID 1,哪个说法准确?
管理和监控 Linux 进程实验让你可以在运行前台和后台命令时观察父进程与子进程 ID。
课程已完成
你已完成 进程创建
现在,你可以追踪经典的 Linux 进程创建顺序。
使用
fork()创建具有不同 PID 的子进程。使用
execve()替换进程映像而不改变 PID。读取 PID 和 PPID 以识别父子关系。
认识 PID 1 和 subreaper 是重新指定父进程后的子进程去向。