进程程序替换
替换原理
用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。
当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。
调用exec并不创建新进程,所以调用exec前后该进程的id并未改变
程序替换就是通过特定的接口,加载磁盘上的一个全新的程序(数据和代码)。加载到调用进程的上下文中(地址空间中),重新改一下页表和物理内存的关系就行,进程替换并没有创建新的子进程
替换函数
其实有六种以exec开头的函数,统称exec函数,本质就是加载程序的函数(从磁盘加载到内存):
#include <unistd.h>` int execl(const char *path, const char *arg, ...); int execlp(const char *file, const char *arg, ...); int execle(const char *path, const char *arg, ...,char *const envp[]); int execv(const char *path, char *const argv[]); int execvp(const char *file, char *const argv[]); int execve(const char *path, char *const argv[], char *const envp[]);
int execl(const char *path,const char *arg,...);
path:路径+目标文件名
…:可变参数列表
函数里可传入多个参数,最后一个参数,必须是NULL,表示参数传递完毕
演示:
注意这里ls的路径
这段程序正常运行
加上execl,注意第一个参数和最后一个参数之间的参数,是我们在命令行上如何使用,就在这里如何书写
在调用excel之后打印出来的内容跟直接调用ls一模一样
execl运行成功以后,原程序的内容便不再执行,我们原始代码里还有一个printf没打印出来
execl是程序替换,调用该函数成功之后,会将当前进程的所有代码和数据进行替换,包括已经执行的和没执行的
所以,一旦调用成功,后续所有代码都不会再执行
故意写一个错代码
execl调用失败
execl函数出错的时候才有返回值,返回值是-1,成功时没有返回值
我们一般这样写,如果执行了exit,则一定调用失败了
替换函数创建子进程
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> int main() { pid_t id = fork(); if(id == 0) { //子进程 printf("子进程开始运行, pid: %d\n", getpid()); execl("/usr/bin/ls","ls","-a","-l",NULL); sleep(3); } else { //父进程 printf("父进程开始运行, pid: %d\n", getpid()); int status = 0; pid_t id = waitpid(-1, &status, 0); //阻塞等待, 一定是子进程先运行完毕,然后父进程获取之后,才退出! if(id > 0) { printf("wait success, exit code: %d\n", WEXITSTATUS(status)); } } return 0; }
阻塞等待, 一定是子进程先运行完毕,然后父进程获取之后,才退出!
创建子进程原因:如果不创建,替换的就是父进程,如果创建了,替换子进程,就不会影响父进程。
当子进程加载新程序,子进程和父进程代码会分离,也就是子进程会写时拷贝
其余替换函数介绍
exec系列函数的功能其实iu是加载器的底层接口
int execv(const char *path,char *const argv[])
path:文件路径+文件名
argv:指针数组,最后要以NULL结尾
execv中的v可看作vector方便记忆。
int execlp(const char *file,const char *arg,...);
file:文件名(要执行谁),execlp,p代表会在path环境中查找
arg:命令行模式下使用格式,命令行参数必须以NULL结尾
int execvp(const char *file,char *const argv[]);
file:文件名(你要执行谁)
argv:指针数组
还有execv系列
函数名带
l(list) : 表示参数采用列表
v(vector) : 参数用数组
p(path) : 有p自动搜索环境变量PATH
e(env) : 表示自己维护环境变量
系统直接调用的是execve,上面的这些函数最终调用的都是excve这个接口,