当我们fork()生成子进程后,子进程的代码与数据可以来自其他可执行程序。把磁盘上其他程序的数据以覆盖的形式给子进程。这样子进程就可以执行全新的程序了,这种现象称为程序替换。

1.进程替换注意事项

1.进程替换不会创建新进程,因为进程替换只是将该进程的数据替换为指定的可执行程序。而进程PCB没有改变,所以不是新的进程,进程替换后不会发生进程pid改变。

2.进程替换后,如果替换成功后则替换函数下的代码不会执行,因为进程替换是覆盖式的替换,替换成功后进程原来的代码就消失了。同理在进程替换失败后会执行替换函数后的代码

3.进程替换函数在进程替换成功后不返回,函数的返回值表示替换失败

4.进程替换成功后,退出码为替换后的进程的退出码

2.替换函数(unistd.h)

execl

函数原型
int execl(const char *path,const char *arg,…)

参数解释
path为可执行程序的路径,arg为如何执行这个可执行程序
…为可变参数,指的是给这执行程序携带的参数,在参数末尾加NULL表示参数结束

返回值:替换失败返回-1,替换成功不返回

eg:
在这里插入图片描述
在这里插入图片描述
观察上图发现进程替换成功后,替换函数下的打印没有执行,原因与注意事项的第二条相同

如果替换失败:
在这里插入图片描述
在这里插入图片描述

execlp

函数原型:
int execlp(const char *file, const char *arg,…)
参数解释:
file:要替换的目标程序,arg:如何执行这个程序,…为给这个程序传的参数。最后以NULL结束表示传参结束

返回值:进程替换失败后返回-1。

相比于execl :execp默认在Linux环境变量PATH中查找可执行程序

Linux环境变量

eg:
在这里插入图片描述

在这里插入图片描述

execv

函数原型:
int execv(const char* path,char* const argv[ ]);

参数解释:
argv数组:保存的是参数列表,将如何执行 可执行程序 和可执行程序需要的参数保存到字符串数组中,最后以NULL结尾表示参数结束,给execv
path:替换目标程序路径,

返回值:进程替换失败返回-1

eg:

在这里插入图片描述
输出结果
可以发现argv数组与main函数的命令行参数相同
Linux_main函数命令行参数

execle

函数原型:
int execle(const char* path, const char* arg, …,char* const envp[ ])
参数解释:
path为替换的目标程序路径,arg与…表示如何执行替换后的程序以NULL结尾。
envp数组为要导入的环境变量
返回值:
替换失败返回-1
eg:
在这里插入图片描述
因为此时我们没有环境变量MYSTR所以第一行打印为空
在这里插入图片描述
这里在myProc子进程中用execle函数来导入环境变量MYSTR
在这里插入图片描述
在这里插入图片描述

注意:
1.导环境变量的数组最后以NULL结尾
2.导入环境变量后原系统环境变量的值被清空,这种导入环境变量的方式为覆盖式导入

替换函数的命名理解(execvp,execve)

替换函数前面的exec不变
l:参数采用列表
v:参数采用数组
p:不需要输入路径,在环境变量自动搜索
e:要导入自己的环境变量

所以execvp表示不需要输入路径,参数用数组传
execve表示需要输入路径,参数用数组传,自己维护环境变量

execvp拓展_简易shell

我们在命令行输入的命令可以用函数fgets来获取
定义一个字符数组
char Shell[100]
fgets(Shell,100,stdin);这样就可以将我们的命令存起来。
因为不同参数之间用空格相隔,所以我们可以用字符串分割函数strtok来分割处理成字符串存到数组中。
strtok函数在没有找到或者最后一次找到时返回NULL,这个函数可以将字符串分割为多个字符串。
execvp:不需要路径,参数用数组传,而strtok分割的正好为命令的参数,我们输入的命令又在环境变量中
所以我们利用进程替换可以实现简易的shell
代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<string.h>

#define MAXLEN 1024
#define LEN 32

int main()
{
  char shell[MAXLEN]={0};
  char* DOS[LEN]={0};//保存处理后的参数
  while(1)
  {
    printf("[dodamce@My_centos dir] ");
    fgets(shell,MAXLEN,stdin);
    shell[strlen(shell)-1]='\0';
    DOS[0]=strtok(shell," ");
    int i=1;
    while(DOS[i]=strtok(NULL," "))//strtok按照空格拆分字符串,并且将其存到DOS字符串数组中
    {
      i++;
    }
  
  pid_t id=fork();//创建子进程
  if(id==0)
  {
    //child
    execvp(DOS[0],DOS);//不需要路径,DOS第一个元素就是我们要替换的可执行程序
    exit(1);
  }
  int status=0;//父进程阻塞等待,接受并打印子进程的退出码,防止僵尸进程
  pid_t wait=waitpid(id,&status,0);
  if(wait>0)
  {
    printf("Exit Code=%d\n",WEXITSTATUS(status));
  }
  }
  return 0;
}

在这里插入图片描述

3.替换函数总结

函数名参数传递形式路径是否导入环境变量
execl列表需要可执行程序路径不导入 使用当前环境变量
execlp列表默认在环境变量中找不导入 使用当前环境变量
execle列表需要可执行程序路径导入 使用导入的环境变量
execv数组需要可执行程序路径不导入 使用当前环境变量
execvp数组默认在环境变量中找不导入 使用当前环境变量
execve数组需要可执行程序路径导入 使用导入的环境变量
Logo

更多推荐