一、进程替换原理
使用fork函数创建子进程,子进程与父进程是共享代码与数据的,此时子进程运行的程序与父进程是相同的(但有可能是不同的分支),如果想让子进程运行自己的独立的程序就需要用到 exec函数执行新的程序。
当一个进程被创建时,操作系统会为其创建进程控制块PCB、地址空间并通过页表将虚拟地址映射到物理内存,而exec系列函数的进程替换原理就是将进程中的代码与数据替换为新的代码和数据,并将页表重新映射,此过程没有创建新的进程,所以exec系列函数调用前后进程pid并未发生改变
二、exec系列函数介绍
以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 execvpe(const char *file, char *const argv[],char *const envp[]);
#函数解释:
1.返回值:exec函数替换成功的返回值是没有意义的,因为若函数替换成功,就会加载新的程序,并执行,并不会返回,只有替换失败时会返回-1,所以exec函数只有失败的返回值没有成功的返回值
2.参数:
- 函数第一个参数为path的是要传入待执行程序的存储路径
- 为file的只需传入待执行程序的程序名即可
- 形参列表中的三个点表示可变参数列表,可以传入不同个数的形参,但最后要以NULL结尾
3.命名理解:
- l(list) : 表示参数采用列表
- v(vector) : 参数用数组
- p(path) : 有p自动搜索环境变量PATH
- e(env) : 表示自己维护环境变量
#函数使用
int execl(const char *path, const char *arg, ...);
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<wait.h>
#include<stdlib.h>
int main()
{
pid_t id=fork();
if(id==0)
{
//child
//形参传列表
execl("/usr/bin/ls","ls","-a","-l",NULL);
}
else
{
int status=0;
pid_t rid= waitpid(id,&status,0);
if(rid>0)
{
printf("wait success child pid:%d child exit code\n",rid,WEXITSTATUS(status));
}
}
}
int execv(const char *path, char *const argv[]);
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<wait.h>
#include<stdlib.h>
int main()
{
pid_t id=fork();
if(id==0)
{
//child
char* const argv[]={
(char*) "ls",
(char*) "-a",
(char*) "-l",
NULL
};
//形参传数组
execv("/usr/bin/ls",argv);
}
else
{
int status=0;
pid_t rid= waitpid(id,&status,0);
if(rid>0)
{
printf("wait success child pid:%d child exit code\n",rid,WEXITSTATUS(status));
}
}
}
int execvp(const char *file, char *const argv[]);
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<wait.h>
#include<stdlib.h>
int main()
{
pid_t id=fork();
if(id==0)
{
//child
char* const argv[]={
(char*) "ls",
(char*) "-a",
(char*) "-l",
NULL
};
//形参只需要传程序名字不需要路径
execvp("ls",argv);
}
else
{
int status=0;
pid_t rid= waitpid(id,&status,0);
if(rid>0)
{
printf("wait success child pid:%d child exit code\n",rid,WEXITSTATUS(status));
}
}
}
int execvpe(const char *file, char *const argv[], char *const envp[]);
其实程序替换不仅可以替换系统命令还可以替换自己的程序,并且exec函数参数中的argv与env就是所谓的命令行参数表和环境变量表,在自己的程序中可以通过main函数传参访问定义的环境变量,当然也可以直接通过environ直接访问所有的环境变量,也可以通过putenv在原有环境变量中添加新的环境变量
exec.c:
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<wait.h>
#include<stdlib.h>
int main()
{
pid_t id=fork();
if(id==0)
{
//child
char* const argv[]=
{
(char*) "mypragma",
NULL
};
char *const envp[]=
{
(char*)"HELLO=111111111111111",
(char*)"HEHE=222222222222222222",
NULL
};
execvpe("./mypragma",argv,envp);
}
else
{
int status=0;
pid_t rid= waitpid(id,&status,0);
if(rid>0)
{
printf("wait success child pid:%d child exit code\n",rid,WEXITSTATUS(status));
}
}
}
mypragma.cc:
#include<iostream>
#include<stdio.h>
using namespace std;
int main(int argc,char* argv[],char* env[])
{
int i=0;
for( ;argv[i];i++)
{
printf("argv[%d]:%s\n",i,argv[i]);
}
printf("----------------------------------------\n");
i=0;
for( ;env[i];i++)
{
printf("env[%d]:%s\n",i,env[i]);
}
cout<<"hello world!\n"<<endl;
cout<<"hello world!\n"<<endl;
cout<<"hello world!\n"<<endl;
cout<<"hello world!\n"<<endl;
}