Linux 进程替换

1.替换原理

用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据 完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程 ,所以调用exec前后该进程的id并未改变。

2.七大替换函数

进程程序替换函数共有七个,其中六个都是在调用函数6,因此函数6 execve 才是真正的系统级接口

这些函数都属于 exec 替换家族,所以它们的返回值都一样。

注意: 这七个函数只有在程序替换失败后才会有返回值,返回 -1,程序替换成功后不返回。

程序都已经替换成功,后续代码也都将被替换,所以成功后的返回值也就没意义了

2.1 execl函数

函数原型:

cpp 复制代码
#include <unistd.h>
 
int execl(const char* path, const char* arg, ...);

1.参数1:准备替换的可执行程序的路径。

2.参数2:准备替换的可执行程序的名字,像系统指令"ls"。

3.参数3:可变参数列表,如"-a","-l",最后一个需要设为NULL来结束

cpp 复制代码
  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<stdlib.h>
  4 int main()
  5 {
  6         printf("before I am a process,pid : %d,ppid : %d\n",getpid(),getppid());
  7         execl("/usr/bin/ls","ls","-l",NULL);
  8 
  9         printf("after I am a process,pid : %d,ppid : %d\n",getpid(),getppid());
 10 }

运行结果:

因为调用execl函数把当前进程替换掉了,所以回去调用系统指令"ls -l",第9行的代码也不会被执行到。

2.2 execv函数

cpp 复制代码
#include <unistd.h>
 
int execv(const char* path, char* const argv[]);

1.参数1:准备替换的可执行程序的路径。

2.参数2:以待替换程序名加后续要执行的指令构成的一张指针数组表。

创建指针数组表时,最后还是要加上NULL表示结束。

cpp 复制代码
  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<stdlib.h>
  4 #include <sys/types.h>
  5 #include <sys/wait.h>
 16 int main()
 17 {
 18         pid_t id=fork();
 19         if(id==0)
 20         {
 21                 char*const a[]=
 22                 {
 23                 "ls",
 24                 "-a",
 25                 "-l",
 26                 NULL
 27                 };
 28         execv("/usr/bin/ls",a);
 29         printf("process replace fail");
 30         exit(-1);
 31         }
 32         pid_t n=wait(NULL);
 33 }

与 execl 函数不同,execv 是以表的形式进行参数传递的。

2.3 execlp函数

可以不用写替换的可执行程序的路径,会去环境变量里面去查找路径。

cpp 复制代码
#include <unistd.h>
 
int execlp(const char* file, const char* arg, ...);

1.参数1:准备替换的可执行程序的路径,需要在环境变量里面。

2.参数2:可参数列表,命令的选项。

cpp 复制代码
 37 int main()
 38 {
 39         pid_t id=fork();
 40         if(id==0)
 41         {
 42                 execlp("ls","ls","-a","-l",NULL);
 43                 printf("process replace fail");
 44                 exit(-1);
 45         }
 46         pid_t n=wait(NULL);
 47 }

运行结果:

使用execlp时,需要保证准备替换的程序路径需要在环境变量中可以被找到。

2.4 execvp函数

cpp 复制代码
#include <unistd.h>
 
int execvp(const char* file, char* const argv[]);

1.参数1:准备替换的可执行程序的路径,需要在环境变量里面。

2.参数2:代替换程序名及其指令选项构成的指针数组,最后要以NULL结尾。

cpp 复制代码
 51 int main()
 52 {
 53         pid_t id=fork();
 54         if(id==0)
 55         {
 56                 char*const argv[]=
 57                 {
 58                         "ls",
 59                         "-a",
 60                         "-l",
 61                         NULL
 62                 };
 63                 execvp("ls",argv);
 64                 printf("process replace fail");
 65                 exit(-1);
 66         }
 67         pid_t n=wait(NULL);
 68 }

运行结果:

2.5 execle函数

如果想要使用自己设置的环境变量可以使用带"e"的exec系列接口。

cpp 复制代码
#include <unistd.h>
 
int execle(const char* path, const char* arg, ..., char* const envp[]);

1.参数1:准备替换的可执行程序的路径。

2.参数2:可参数列表,命令的选项。

3.参数3:环境变量参数

1.父进程设置环境变量后 fork 子进程,子进程通过 execle 替换为自定义程序并传递环境变量。

cpp 复制代码
  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<sys/types.h>
  4 #include<sys/wait.h>
  5 #include<stdlib.h>
  6 int main()
  7 {
  8 
  9         putenv("wjp=666");
 10         pid_t id=fork();
 11         if(id==0)
 12         {
 13                 sleep(2);
 14                 extern char**environ;
 15 //              char*const myarg[]={
 16 //              "test",
 17 //              "-a",
 18 //              NULL};
 19         //      execlp("pwd","pwd",NULL);
 20                 execle("./test","test","-a",NULL,environ);
 21         }
 22         printf("I am father,pid:%d,ppid:%d\n",getpid(),getppid());
 23         pid_t rid=wait(NULL);
 24         return 0;
 25 }   
cpp 复制代码
 69 #include<stdio.h>
 70 int main(int argc,char*argv[],char*env[])
 71 {
 72         printf("running begin\n");
 73         for(int i=0;argv[i];i++)
 74         {
 75                 printf("%d : %s\n",i,argv[i]);
 76         }
 77         printf("-----------------------------------------");
 78         for(int i=0;env[i];i++)
 79         {
 80                 printf("%d : %s\n",i,env[i]);
 81         }
 82         return 0;
 83 }

通过进程1创建子进程,把进程1的子进程替换为进程2。

2.替换成自己的环境变量

cpp 复制代码
  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<sys/types.h>
  4 #include<sys/wait.h>
  5 #include<stdlib.h>
  6 int main()
  7 {
  8 
  9 //      putenv("wjp=666");
 10         pid_t id=fork();
 11         if(id==0)
 12         {
 13                 sleep(2);
 14                 char*const myenv[]=
 15                 {
 16                         "myval=100",
 17                         NULL
 18                 };
 19 //              extern char**environ;
 20 //              char*const myarg[]={
 21 //              "test",
 22 //              "-a",
 23 //              NULL};
 24                 execle("./test","test","-a",NULL,myenv);
 25         }
 26         printf("I am father,pid:%d,ppid:%d\n",getpid(),getppid());
 27         pid_t rid=wait(NULL);
 28         return 0;
 29 }
cpp 复制代码
 69 #include<stdio.h>
 70 int main(int argc,char*argv[],char*env[])
 71 {
 72         printf("running begin\n");
 73         for(int i=0;argv[i];i++)
 74         {
 75                 printf("%d : %s\n",i,argv[i]);
 76         }
 77         printf("-----------------------------------------");
 78         for(int i=0;env[i];i++)
 79         {
 80                 printf("%d : %s\n",i,env[i]);
 81         }
 82         return 0;
 83 }

运行结果:

替换的进程的环境变量被覆盖成了传递过去的环境变量。

2.6 execve函数

execve才是系统调用,上面的5个函数都是封装了execve。

cpp 复制代码
#include <unistd.h>
 
int execve(const char* filename, char* const argv[], char* const envp[]);

1.参数1:代替换的程序路径

2.参数2:代替换的程序名及其命令选项构成的指针数组。

3.参数3:传递给替换程序的环境变量表。

环境变量:

execl,execv,execlp,execvp这四个函数的环境变量都会继承父进程的环境变量,环境变量也就不会改变,想要添加或者修改可以是用putenv来添加或者修改。

没有则添加,有则修改。

示例:

cpp 复制代码
putenv("wjp=666");

如果需要覆盖使用自己的环境变量可以使用execle和execve带"e"的接口。

现在可以理解为什么在 bash 中创建程序并运行,程序能继承 bash 中的环境变量表了

在 bash 下执行程序,等价于在 bash 下替换子进程为指定程序,并将 bash 中的环境变量表 environ 传递给指定程序使用

其他没有带 e 的替换函数,默认传递当前程序中的环境变量表

exec系列的所有可变参数列表最后都会传给替换程序的main函数的argv数组

3.小结

这些函数原型看起来很容易混,但只要掌握了规律就很好记。

l(list) : 表示参数采用列表,就需要将指令选项一个一个传递过去

v(vector) : 参数用数组,把指令选项放入到自己定义的argv数组里面,传递。

p(path) : 有p自动搜索环境变量PATH,系统指令可以不用加路径。

e(env) : 表示自己维护环境变量。

exec系列的所有可变参数列表最后都会传给替换程序的main函数的argv数组

相关推荐
松涛和鸣1 小时前
23、链式栈(LinkStack)的实现与多场景应用
linux·c语言·c++·嵌入式硬件·ubuntu
赖small强1 小时前
【Linux驱动开发】Linux Debugfs 虚拟文件系统深度解析与实战指南
linux·驱动开发·debugfs
Crazy________1 小时前
43ansible常用模块及变量定义方式
linux·运维·服务器
翼龙云_cloud1 小时前
阿里云渠道商:无影云电脑常见问题及其解决方法有哪些?
运维·服务器·阿里云·云计算·电脑
小白电脑技术1 小时前
飞牛NAS最近好奇怪啊?一段时间不重启,内存几乎快用完了?
linux·电脑
北珣.1 小时前
docker容器-命令
运维·docker·容器
幸运小猿1 小时前
启动项目报错,zookeeper影响的
linux·运维·服务器
liu****1 小时前
11.字符函数和字符串函数(一)
linux·运维·c语言·开发语言·数据结构·算法
honsor1 小时前
一种采用POE供电的RJ45网络型温湿度传感器
运维·服务器·网络