欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 资讯 > Linux进程程序替换(14)

Linux进程程序替换(14)

2025/4/18 21:19:26 来源:https://blog.csdn.net/2301_80392199/article/details/147098244  浏览:    关键词:Linux进程程序替换(14)

文章目录

  • 前言
  • 一、替换原理
  • 二、替换函数
  • 三、函数解释
  • 四、命名理解
  • 总结


前言

  加油加油!!!


一、替换原理

  用 fork 创建子进程后,子进程执行的是和父进程相同的程序(但有可能执行不同的代码分支),若想让子进程执行另一个程序,往往需要调用一种 exec 函数。

  当进程调用一种 exec 函数时,该进程的用户空间代码和数据完全被新程序替换,并从新程序的启动例程开始执行。

在这里插入图片描述

当进行进程程序替换时,有没有创建新的进程?

  进程程序替换之后,该进程对应的PCB、进程地址空间以及页表等数据结构都没有发生改变,只是进程在物理内存当中的数据和代码发生了改变,所以并没有创建新的进程,而且进程程序替换前后该进程的pid并没有改变。

子进程进行进程程序替换后,会影响父进程的代码和数据吗?

  子进程刚被创建时,与父进程共享代码和数据,但当子进程需要进行进程程序替换时,也就意味着子进程需要对其数据和代码进行写入操作,这时便需要将父子进程共享的代码和数据进行写时拷贝,此后父子进程的代码和数据也就分离了,因此子进程进行程序替换后不会影响父进程的代码和数据

二、替换函数

#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[]);

  替换函数有六种以 exec 开头的函数,它们统称为 exec 函数:

一、int execl(const char *path, const char *arg, …);

  第一个参数是要执行程序的路径,第二个参数是可变参数列表,表示你要如何执行这个程序,并以NULL结尾。

  例如,要执行的是ls程序

在这里插入图片描述

#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 是以 链式 的方式进行传递的
在这里插入图片描述

execl("/usr/bin/ls", "ls", "-a", "-i", "-l", NULL);

二、int execlp(const char *file, const char *arg, …);

  第一个参数是要执行程序的名字,第二个参数是可变参数列表,表示你要如何执行这个程序,并以NULL结尾

  例如,要执行的是ls程序
在这里插入图片描述

#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;
}

在这里插入图片描述

execlp("ls", "ls", "-a", "-i", "-l", NULL);

三、int execle(const char *path, const char *arg, …, char *const envp[]);

  第一个参数是要执行程序的路径,第二个参数是可变参数列表,表示你要如何执行这个程序,并以NULL结尾,第三个参数是你自己设置的环境变量

  例如,你设置了 MYVAL 环境变量,在 mycmd 程序内部就可以使用该环境变量。

我们可以思考一个问题:为什么在 bash 中创建程序并运行,程序能继承 bash 中的环境变量表了?

  • 在 bash 下执行程序,等价于在 bash 下替换子进程为指定程序,并将 bash 中的环境变量表 environ 传递给指定程序使用
  • 其他没有带 e 的替换函数,默认传递当前程序中的环境变量表
char* myenvp[] = { "MYVAL=2021", NULL };
execle("./mycmd", "mycmd", NULL, myenvp);

四、int execv(const char *path, char *const argv[]);

  第一个参数是要执行程序的路径,第二个参数是一个指针数组,数组当中的内容表示你要如何执行这个程序,数组以NULL结尾。

  例如,要执行的是ls程序

char* myargv[] = { "ls", "-a", "-i", "-l", NULL };
execv("/usr/bin/ls", myargv);

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

  第一个参数是要执行程序的名字,第二个参数是一个指针数组,数组当中的内容表示你要如何执行这个程序,数组以NULL结尾。

  例如,要执行的是ls程序

char* myargv[] = { "ls", "-a", "-i", "-l", NULL };
execvp("ls", myargv);

六、int execve(const char *path, char *const argv[], char *const envp[]);

  第一个参数是要执行程序的路径,第二个参数是一个指针数组,数组当中的内容表示你要如何执行这个程序,数组以NULL结尾,第三个参数是你自己设置的环境变量。

  例如,你设置了 MYVAL 环境变量,在 mycmd 程序内部就可以使用该环境变量。

char* myargv[] = { "mycmd", NULL };
char* myenvp[] = { "MYVAL=2021", NULL };
execve("./mycmd", myargv, myenvp);

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

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

三、函数解释

  • 这些函数如果调用成功,则加载指定的程序并从启动代码开始执行,不再返回。
  • 如果调用出错,则返回-1

  也就是说,exec系列函数只要返回了,就意味着调用失败。

四、命名理解

这六个 exec 系列函数的函数名都以 exec 开头,其后缀的含义如下:

  • l(list):表示参数采用列表的形式,一一列出。
  • v(vector):表示参数采用数组的形式。
  • p(path):表示能自动搜索环境变量PATH,进行程序查找。
  • e(env):表示可以传入自己设置的环境变量。
函数名参数格式是否带路径是否使用当前环境变量
execl列表
execlp列表
execle列表否,需自己组装环境变量
execv数组
execvp数组
execve数组否,需自己组装环境变量

  事实上,只有 execve 才是真正的系统调用,其它五个函数最终都是调用的 execve ,所以 execve 在 man 手册的第2节,而其它五个函数在 man 手册的第3节,也就是说其他五个函数实际上是对系统调用 execve 进行了封装,以满足不同用户的不同调用场景的。

下图为exec系列函数族之间的关系:

在这里插入图片描述

  • 这就表明程序替换并不是进程替换
  • 因为是同一个进程,所以对父进程没有任何影响,体现了进程间的独立性

总结

  下一篇给大家带来综合的训练 —— 实现一个自己的 Shell

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词