操作系统

进程的诞生:fork + exec + wait

在终端敲下一个命令,bash 是怎样把它变成一个新运行的程序的。

概况

以在 Linux 的 bash 里输入 ls 为例。

一句话总结
fork 复制自己,exec 换成新程序,wait 等它结束
什么是进程
正在运行的程序:代码 + 内存 + 打开的文件 + 运行状态
为什么分两步
fork 和 exec 之间,可以先布置子进程(如重定向输出)
常见用法
shell 运行命令、管道 a | b、Node.js 的 child_process
收尾
父进程不 wait,子进程会变成僵尸进程
进程状态
运行(R)、睡眠(S/D)、停止(T)、僵尸(Z)
进程 vs 线程
Linux 上都用 clone 创建:线程共享地址空间,进程各有一份
posix_spawn / vfork
不需要在中间布置时,可一步创建并执行,开销更小

步骤 6 / 6

孤儿进程与信号

父进程先走了,孩子由别的进程收养;进程之间还能用信号打招呼。

如果父进程先退出,子进程就成了“孤儿”,内核把它过继给 init(systemd,PID 1)或指定的“收养者”,由它负责 wait 回收。 进程之间可以发信号:Ctrl+C 发 SIGINT,kill 默认发 SIGTERM,子进程结束时父进程会收到 SIGCHLD。 进程可以自己处理大多数信号,但 SIGKILL 无法捕获,一定会被杀掉。

父进程
内核
子进程
bash(父进程)
内核
子进程
收养:新父进程 = PID 1
事件日志(6)
当前状态发生了什么
bash 运行中用户输入 ls,bash 需要启动一个新程序
父子两个进程内核复制出子进程
子进程运行 ls子进程变成了 ls
bash 等待中父进程睡眠,等待子进程
bash 继续运行子进程被回收,显示下一个提示符
回收孤儿被 init 收养并回收