linux进程控制【程序替换】

目录

前言:

1.替换原理

​编辑 2.替换函数

2.1函数 execl

2.2函数 execv

2.3函数 execlp

2.4函数 execvp

2.5函数 execle

2.6函数 execve

2.7函数 execvpe



前言:

前面我们介绍了进程控制中的创建,退出等待,本章节我们将继续介绍一下进程替换,以上就是关于 Linux进程控制(创建、终止、等待) 的相关知识了,我们学习了 子进程 是如何被创建的,创建后又是如何终止的,以及 子进程 终止 父进程 需要做些什么,有了这些知识后,在对 进程 进行操作时能更加灵活和全面。

1.替换原理

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

 2.替换函数

 其实有六种以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 execve(const char *path, char *const argv[], char *const envp[]);

 

函数解释:

  • 这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。
  • 如果调用出错则返回-1
  • 所以exec函数只有出错的返回值而没有成功的返回值。

命名理解: 

  • l(list) : 表示参数采用列表
  • v(vector) : 参数用数组
  • p(path) : p自动搜索环境变量PATH
  • e(env) : 表示自己维护环境变量

 

 

2.1函数 execl

 首先是最简单的替换函数 execl

 

#include <unistd.h>

int execl(const char* path, const char* arg, ...);

函数解读

  • 返回值:替换失败返回 -1
  • 参数1:待替换程序的路径,如 /usr/bin/ls
  • 参数2:待替换程序的名称,如 ls
  • 参数3~N:待替换程序的选项,如 -a -l等,最后一个参数为 NULL,表示选项传递结束
  • ... 表示可变参数列表,可以传递多个参数

注意: 参数选项传递结束或不传递参数,都要在最后加上 NULL,类似于字符串的 '\0'

 

#include <stdio.h>
#include <unistd.h>

int main()
{
  //execl 函数
  printf("程序替换前,you can see me\n");
  int ret = execl("/usr/bin/ls", "ls", "-a", "-l", NULL);

  //程序替换多发生于子进程,也可以通过子进程的退出码来判断是否替换成功
  if(ret == -1)
    printf("程序替换失败!\n");

  printf("程序替换后,you can see me again?\n");
  return 0;
}

 可以看出,函数 execl 中的 命令+选项+NULL 是以 链式 的方式进行传递的,方便理解。

 

2.2函数 execv

 替换函数 execv 是以顺序表 vector 的方式传递 参数2~N 的

 

函数解读

  • 返回值:替换失败返回 -1
  • 参数1:待替换程序的路径,如 /usr/bin/ls
  • 参数2:待替换程序名及其命名构成的 指针数组,相当于一张表

 

注意: 虽然 execv 只需传递两个参数,但在创建 argv 表时,最后一个元素仍然要为 NULL 

#include <stdio.h>
#include <stdlib.h> //exit 函数头文件
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
  //execv 函数
  pid_t id = fork();
  if(id == 0)
  {
    printf("子进程创建成功 PID:%d   PPID:%d\n", getpid(), getppid());
    char* const argv[] = 
    {
      "ls",
      "-a",
      "-l",
      NULL
    };	//argv 表,实际为指针数组

    execv("/usr/bin/ls", argv);

    printf("程序替换失败\n");
    exit(-1); //如果子进程有此退出码,说明替换失败
  }

  int status = 0;
  waitpid(id, &status, 0); //父进程阻塞等待
  if(WEXITSTATUS(status) != 255)
  {
    printf("子进程替换成功,程序正常运行 exit_code:%d\n", WEXITSTATUS(status));
  }
  else
  {
    printf("子进程替换失败,异常终止 exit_code:%d\n", WEXITSTATUS(status));
  }

  return 0;
}

运行结果:

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

 

2.3函数 execlp

可能有的人觉得写 path 路径很麻烦,还有可能会写错,那么能否换成 自动挡 替换呢?

答案是可以的,execlp 函数在进行程序替换时,可以不用写 path 路径

 

#include <unistd.h>

int execlp(const char* file, const char* arg, ...);

execlp 就像是 execl 的升级版,可以自动到 PATH 变量中查找程序

注意: 只能在环境变量表中的 PATH 变量中搜索,如果待程序路径没有在 PATH 变量中,是无法进行替换的

#include <stdio.h>
#include <stdlib.h> //exit 函数头文件
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
  //execlp 函数
  pid_t id = fork();
  if(id == 0)
  {
    printf("you can see me\n");

    execlp("ls", "ls", "-a", "-l", NULL); //程序替换

    printf("you can see me again?");
    exit(-1);
  }

  int status = 0;
  waitpid(id, &status, 0);  //等待阻塞
  if(WEXITSTATUS(status) != 255)
    printf("子进程替换成功 exit_code:%d\n", WEXITSTATUS(status));
  else
    printf("子进程替换失败 exit_code:%d\n", WEXITSTATUS(status));

  return 0;
}

使用 execlp 替换程序更加方便,只要待替换程序路径位于 PATH 中,就不会替换失败

2.4函数 execvp

 execv 加个 p 也能实现自动查询替换,即 execvp

 

#include <unistd.h>

int execvp(const char* file, char* const argv[]);

函数解读

  • 返回值:替换失败返回 -1
  • 参数1:待替换程序名,需要位于 PATH 中
  • 参数2:待替换程序名及其命名构成的 指针数组

 

#include <stdio.h>
#include <stdlib.h> //exit 函数头文件
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
  //execvp 函数
  pid_t id = fork();
  if(id == 0)
  {
    printf("子进程创建成功 PID:%d   PPID:%d\n", getpid(), getppid());
    char* const argv[] = 
    {
      "ls",
      "-a",
      "-l",
      NULL
    };

    execvp("ls", argv);

    printf("程序替换失败\n");
    exit(-1); //如果子进程有此退出码,说明替换失败
  }

  int status = 0;
  waitpid(id, &status, 0); //父进程阻塞等待
  if(WEXITSTATUS(status) != 255)
  {
    printf("子进程替换成功,程序正常运行 exit_code:%d\n", WEXITSTATUS(status));
  }
  else
  {
    printf("子进程替换失败,异常终止 exit_code:%d\n", WEXITSTATUS(status));
  }

  return 0;
}

 

 

2.5函数 execle

 e 表示 env 环境变量表,可以将自定义或当前程序中的环境变量表传给待替换程序

 

#include <unistd.h>

int execl(const char* path, const char* arg, ..., char* const envp[]);

函数解读

  • 最后一个参数:替换成功后,待替换程序的环境变量表,可以自定义
char* const myenv[] = {"myval=100", NULL};  //自定义环境变量表

execle("./other/CPP", NULL, myenv); //程序替换

替换为自己写的程序 CPP

 

//当前源文件为 test.cc 即 C++源文件
// .xx 后缀也可以表示 C++源文件
#include <iostream>

using namespace std;

extern char** environ;	//声明环境变量表

int main()
{
  int pos = 0;
  //只打印5条
  while(environ[pos] && pos < 5)
  {
    cout << environ[pos++] << endl;
  }
  return 0;
}

 

可以看到,程序 CPP 中的环境变量表变成了自定义环境变量,即只有一个环境变量 myval=100

改变 execle 最后一个参数,传入默认环境变量表

 extern char** environ;

 execle("./other/CPP", NULL, environ); //继承环境变量表

结论: 如果主动传入环境变量后,待替换程序中的原环境变量表将被覆盖

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

在 bash 下执行程序,等价于在 bash 下替换子进程为指定程序,并将 bash 中的环境变量表 environ 传递给指定程序使用
其他没有带 e 的替换函数,默认传递当前程序中的环境变量表

 

 

2.6函数 execve

execve 是系统真正提供的程序替换函数,其他替换函数都是在调用 execve

比如

execl 相当于将链式信息转化为 argv 表,供 execve 参数2使用
execlp 相当于在 PATH 中找到目标路径信息后,传给 execve 参数1使用
execle 的 envp 最终也是传给 execve 中的参数3

 

#include <unistd.h>

int execve(const char* filename, char* const argv[], char* const envp[]);

函数解读

  • 返回值:替换失败返回 -1
  • 参数1:待替换程序的路径
  • 参数2:待替换程序名及其参数组成的 argv 表
  • 参数3:传递给待替换程序的环境变量表

替换 ls -a -l 程序

extern char** environ;

execve("/usr/bin/ls", argv, environ);

替换为自定义程序 CPP

extern char** environ;

execve("./other/CPP", argv, environ);

 替换函数除了能替换为 C++ 编写的程序外,还能替换为其他语言编写的程序,如 JavaPythonPHP等等,虽然它们在语法上各不相同,但在 OS 看来都属于 可执行程序,数据位于 代码段 和 数据段,直接替换即可

系统级接口是不分语言的,因为不论什么语言最终都需要调用系统级接口,比如文件流操作中的 openclosewrite 等函数,无论什么语言的文件流操作函数都需要调用它们 

2.7函数 execvpe

对 execvp 的再一层封装,使用方法与 execvp 一致,不过最后一个参数可以传递环境变量表

#include <unistd.h>

int execvpe(const char* file, char* const argv[], char* const envp[]);

函数解读

  • 返回值:替换失败返回 -1
  • 参数1:待替换程序名,需要位于 PATH 中
  • 参数2:待替换程序名及其命名构成的 指针数组
  • 参数3:传递给待替换程序的环境变量表
extern char** environ;

execvpe("ls", argv, environ);

 

  • 13
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值