一:最简单的看看程序替换是什么样的(单个进程版)
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 pid9 10 if(id==0) //child process11 {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 process19 pid_t ret = waitpid(id,NULL,0); //child process pid , status , WNOHANG20 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
会尝试执行它。