一:最简单的看看程序替换是什么样的(单个进程版)
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4 int main()
5 {
6 printf("Before : I am a process , myPid:%d,myPPid:%d\n",getpid(),getppid());
7
8 execl("/usr/bin/top","top",NULL);
9
10 printf("After : I am a process , myPid:%d,myPPid:%d\n",getpid(),getppid());
11
12 return 0;
13 }
程序在调用execl
之后不能打印"After"信息,因为一旦execl
被调用,当前的进程映像将被替换,因此第二个print中的代码将不会被执行。
二:进程替换的原理
用fork创建子进程后执行的是和父进程相同的程序,子进程往往要调用一种exec函数以执行另一个程序。
当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动
例程开始执行。
调用exec并不创建新进程,所以调用exec前后该进程的id并未改变
三:验证各种程序替换的接口(多进程版)
创建一个子进程,并使用execl
函数来替换子进程的映像,执行/usr/bin/ls -l -a
命令。
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4 #include<sys/types.h>
5 #include<sys/wait.h>
6 int main()
7 {
8 pid_t id =fork(); //child process return 0 , parent process return child pid
9
10 if(id==0) //child process
11 {
12 printf("Before : I am a process , myPid:%d,myPPid:%d\n",getpid(),getppid());
13 execl("/usr/bin/ls","-l","-a",NULL);
14 printf("After : I am a process , myPid:%d,myPPid:%d\n",getpid(),getppid());
15 exit(0);
16 }
17
18 //parent process
19 pid_t ret = waitpid(id,NULL,0); //child process pid , status , WNOHANG
20 if(ret>0)
21 {
22 printf("wait sucess,father pid:%d,wait child pid:%d\n",getpid(),ret);
23 }
24 return 0;
25 }
~
execl
会替换当前子进程的映像,包括程序的代码和数据
当改子进程程序替换之后,该子进程对应的PCB、进程地址空间以及页表等数据结构都没有发
生改变,对应的execl退出,会继续被父进程等待收回
四:总结
替换函数:
其实有六种以exec开头的函数,统称exec函数
exec参数的上传就如命令行一般,命令行怎么打,参数就怎么加,第一个参数为程序的路径,最后一个参数有再补个NULL即可
函数解释:
这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。如果调用出错则返回-1
所以exec函数只有出错的返回值而没有成功的返回值。
// 尝试执行ls命令
if (execl("/usr/bin/ls", "ls", "-l", "-a", NULL) == -1) {
// 如果execl失败,打印错误信息并退出
perror("execl failed");
exit(EXIT_FAILURE); // 使用非零值退出,表示程序因错误而终止
}
// 如果execl成功,if里面的代码不会被执行
命名理解:
这些函数原型看起来很容易混,但只要掌握了规律就很好记。
execl的list链表与execv的vector数组的代码示例:
execl与execlp是否带路径代码示例:
execl
函数需要你提供程序的完整路径作为第一个参数
execlp
函数使用程序名来搜索程序的路径,不需要提供完整路径。它使用环境变量PATH($PATH)
来查找程序,如果PATH
环境变量包含的目录中有与程序名匹配的可执行文件,execlp
会尝试执行它。