
exec替换exec替换其实就是进程替换:exec()系列包含如下六个:execl,execlp,execle,execv,execvp这五个是库函数这五个底层都是调用execve实现的execve是系统调用本质它们是没什么区别的仅仅是参数形式不一样。进程替换其实就是把一个原有的进程正在运行的进程换成了另外一个进程。Linux 创建进程经典组合:fork()exec()fork() COW (写时拷贝): fork 创建子进程不会直接拷贝全部内存。父子初始共享物理内存仅复制页表内存页标记只读。 只有一方修改数据才会拷贝对应页面做到不写不复制降低时间与内存开销。exec 程序替换 exec 不会创建新进程PID 不变直接覆盖当前进程的代码、数据段加载新程序执行。核心搭配逻辑 -- fork()生成子进程后子进程立刻调用 exec几乎不修改原有内存COW 无需拷贝页面直接丢弃旧内存加载新程序效率拉满。如果还不理解的话可想象成你是父进程你在看书fork一下就会出现你的朋友也要看书如果没有COW那么你就要把你的书完整复制一本给你的朋友如果有了COW那你们可以看同一本书只看不写除非有一个人要修改那也只把修改的一页复印一份。exec 程序替换代码示例解析示例1这段代码演示execl实现 exec 程序替换。程序先打印自身 PID随后调用execl将当前进程正在运行的程序替换为ps -f命令。exec 替换的关键特性替换成功后函数不返回原程序后续代码直接丢弃因此execl之后的 printf 语句永远不会执行。整个过程进程 PID 保持不变仅仅覆盖进程内存里的代码段与数据段加载新程序运行。只有 execl 调用失败才会继续执行后面代码。日常开发中 exec 一般配合 fork 使用fork 创建子进程再在子进程调用 exec避免把父进程本身替换掉。在这里execl末尾的(char*)0是参数结束标记。execl 属于可变参数函数要求命令参数列表必须以空指针结尾。(char*)0就是将 0 强制转为 char 类型空指针告诉函数参数列表到此结束如果缺少这个标记程序会读取内存里的脏数据造成未知错误。示例2我们来看一下上述代码的另一变种形式:为什么这个没有写路径呢我们系统有一个环境变量:PATH这个环境变量在Windows上也有而execlp末尾的P就是PATH的意思。execlp会自动去PATH 环境变量记录的目录列表里依次查找第一个参数写的程序名(这里就是ps)。而我们可以通过下图看出终端命令echo $PATH打印出一串用:分隔的目录/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:...which ps查询到ps命令在/usr/bin/psexeclp就会遍历 PATH在/usr/bin找到ps然后执行。关于环境变量有没有觉得一丝熟悉呢之前我们曾说过主函数的第三个参数就是环境变量下面就是完整的main原型:int main(int argc, char *argv[], char *envp[])这里的envp就是环境变量数组PATH就在里面保存。示例3接下来演示的是需要传环境变量的我们通过帮助手册也可以看到:当然我们也要学会观察它的参数int execle(const char *pathname, const char *arg, ..., (char *) NULL, char *const envp[])execle名字拆解eenvironment代表可以手动传递环境变量llist参数用列表逐个写最后(char*)0标记参数结束/usr/bin/ps必须写完整路径没有 p不会自动搜 PATHpsargv [0]程序名-f传给 ps 的命令参数(char*)0命令行参数结束标志envp自定义环境变量数组这里直接把 main 收到的环境变量传给 ps但为什么前面两个示例看似没有传环境变量呢因为前两个不手动传环境变量时默认继承调用进程当前程序的环境变量底层会自动把当前进程的environ全局环境变量传给新程序。而execle/execvpe需要手动指定环境变量你传什么新程序就用什么如果你不传新程序就没有环境变量。示例4为什么要加一个这样的数组这是因为execv函数名称中的v代表vector数组和带l的exec函数采用逐个罗列参数的传参方式不同execv要求把要传递给新程序的所有命令行参数预先存入字符串数组myargv中数组最后必须放置0或NULL标记参数结束再将这个数组作为第二个参数传入execv第一个参数需要填写可执行文件的完整路径函数执行成功就会替换当前进程失败才会继续执行后续错误打印代码。简单点来说就是带l的exec函数参数直接一个个写在函数括号里。 而execv是带v的不能一个一个写参数必须把所有参数先放到数组里再把数组传给它数组最后要写0表示参数结束所以这里要创建myargv数组。执行成功就替换进程失败才跑后面打印错误的代码。示例5这个execvp示例是用来演示同时带v和p特性的exec函数v代表需要提前把命令参数放到数组里传递p代表会自动在PATH环境变量中搜索程序不用写程序完整路径函数执行成功就替换当前进程只有执行失败时才会运行后面打印错误的代码用来和execv做对比。示例6在这里我们通过man手册也可发现上面五个示例都是(3)库函数而下面的execve是(2)系统调用然后我们来执行一下test.c看一下结果在exec 系列函数中execve是最基础的系统调用其余 exec 库函数底层最终都会调用它。execve同时具备v数组传参和e自定义环境变量两个特性。通过上述结果也可看出程序运行时首先打印当前进程的PID调用execve成功后会直接用ps程序替换当前进程进程PID不会改变原main函数剩下的代码不会执行输出的进程信息中可以看到该PID对应的程序已经变为ps -f只有execve调用失败的情况下才会执行后续打印错误信息的代码。上述就是exec()的全部系列最后祝大家国庆快乐哦