进程程序替换
通过上一节的学习,我们掌握了如何使用 wait 和 waitpid 来回收子进程资源,避免僵尸进程的产生。然而,创建子进程的初衷不仅仅是为了让它复制一份父进程的代码执行相同操作。在绝大多数实际应用中,创建子进程主要有两个目的:
- 一个父进程希望复制自己,使父子进程同时执行不同的代码段。
- 一个进程要执行一个不同的程序。例如子进程从 fork 返回后,调用 exec 函数。
原理
程序替换的过程没有必要创建新的进程。通过将代码和数据加载到内存里,覆盖代码和数据段。而 PCB、虚拟内存空间、页表不会有太大变化,顶多修改页表的映射关系。因此没必要创建一个新的进程(打印程序替换前后的 pid 即可证明)。

用 fork 创建子进程后执行的是和父进程相同的程序,子进程往往要调用一种 exec 函数以执行另一个程序。当进程调用一种 exec 函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用 exec 并不创建新进程,所以调用 exec 前后该进程的 id 并未改变。
exec 函数族的核心功能是用一个新的程序替换当前进程的映像。这个功能本身是明确的,但在实际应用时,我们调用新程序的需求却是多变的:命令有时需要带上复杂的参数列表,例如 ls -l -a -i。
为了理解 exec 系列的函数,这里需要先提一嘴可变参数,只有理解了可变参数,才能方便引入接下来的 exec 系列函数。
替换操作
理解可变参数
可变参数函数是指能够接受数量不确定的参数函数。那又是如何做到接受不确定参数数量的呢?毫无疑问,肯定需要一个"路标"指引它从哪里开始走,要走多少步啊!
"路标":就是函数里的第一个固定参数 count。它的最大作用就是告诉函数:'从我后面开始,一共有 count 个参数'。
va_start:根据最后一个固定参数,找到可变参数的起始位置。va_arg:取出当前位置的一个值;把位置移动到下一个参数那里。va_end:打扫战场,清理工作。
int execl(const char *pathname, const char *arg, ... /*, (char *) NULL */);
int execlp(const char *file, const char *arg, ... /*, (char *) NULL */);
int execle(const char *pathname, const char *arg, ... );
;
;
;












