【Linux系统编程】进程程序替换

 介绍:

        进程程序替换是指将一个进程中正在运行的程序替换为另一个全新的程序的过程,但替换不是创建新进程,只是将对应程序的代码和数据进行替换。具体来说,这个替换过程涉及将磁盘中的新程序加载到内存结构中,并重新建立页表映射,然后更新一系列的组件,使得执行程序替换的进程(如子进程)与新程序关联起来。这样,该进程就不再执行原来的程序,而是开始执行新的程序。

        进程替换的使用在很多情况下都是使用子进程来完成。当我们想让一个子进程执行与父进程不同的代码片段时,就可以通过进程程序替换来实现,这时父进程完成自己的程序,子进程进行替换。这里需说明一下,子进程的进程替换不会影响父进程,因为进程具有独立性。当刚开始创建子进程时,子进程内部的指针指向父进程的数据和代码,子进程一旦发生替换时(改动了原本的数据),要替换的部分将会进行写时拷贝,开辟一块空间。

替换原理

        通常,用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支)。子进程进行替换往往要调用一种exec函数,以便在子进程中执行另一个程序,而父进程可以继续执行其原始任务。

        这里说明一下exec函数,当该进程调用exec函数时,系统就会进行程序替换,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行,将新的代码和数据替换到原本的进程结构中。这里注重强调一下,调用exec进行进程替换并不创建新进程,替换的本质就是加载,将磁盘上的数据和代码加载到内存中,从而更新一系列数据重新建立起关系,所以调用exec前后该进程的 id 并未改变。

        我们来研究一下exec替换函数。此函数共有以下六种形式,统称exec函数:

头文件

#include <unistd.h>

exec* 函数

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* 函数的共性

1,程序一旦被 exec* 替换成功,exec* 后续的代码不在执行,因为此时的进程被替换掉了。若替换失败,此进程后面的代码才继续执行。

2,exec* 只有失败返回值-1,没有成功返回值,因为一旦成功此进程将被替换。

3,替换完成,不会创建新的进程,即PCB结构。

        exec* 的各种类型函数中,虽说各有各的不同,但是这里只要明白里面的各种参数功能,就能够理解exec* 函数是如何进行替换的。

        下面的演示为了方便,这里替换的进程统一用Linux系统下的指令。这里先说明一下,在Linux中,指令本质上是程序,各种命令实际上都是可执行程序。当进程执行时,它会加载程序到内存中,并通过虚拟地址空间与物理内存之间的映射关系来执行这些指令。

形式一:

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

path: 要替换程序的路径。

arg:表示要替换的进程程序,这里参数可以有多个,用于指定程序的输入、选项或其他必要的信息,但最后必须以NULL结尾,以标记参数列表结束。

例:execl("/usr/bin/ls", "ls", "-l", "-a", NULL);    将此时进程替换成在路径 "/usr/bin/ls" 下的 ls -l -a 进程,若在此路径下不存在指定的进程,则替换失败,如:execl("/usr/bin/l", "ls", "-l", "-a", NULL);  没有此路径,替换失败。

形式二:

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

file:这是你要执行的程序的名称(即可执行文件名)。如果 file 中不包含路径信息(即只是程序名而不是完整的路径),则会在 PATH 环境变量中定义的目录列表中查找该程序。

arg:与execl中的arg一样,表示要替换的进程程序。

例:execlp("ls", "ls", "-a", "-l", NULL);      将此时进程替换成 ls -a -l 进程。注意,这里的两个参数 "ls" 不重复,各自表示的含义不一样。

        在exec*的各种形式函数中,后面带 p 表示 PATH 环境变量,这时只用传达进程名称即可,不用告诉系统程序在哪里,系统在替换时会自动去PATH环境变量中查找。

形式三:

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

argv[]:用指针数组argv来表示替换的进程程序。

例:char* argv = { "ls", "-a", "-l" };   execv("/usr/bin/ls", argv);   效果与上相同。

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

例:char* argv = { "ls", "-a", "-l" };    execvp("ls", argv);   效果类同

         在exec*的各种形式函数中,后面带 v 的表示使用指针数组的形式表示要进行替换的进程程序。后面带 l 的表示以参数列表的形式表示要进行替换的进程程序。

形式四:

        exec* 函数后面带 e 的表示环境变量。至于为什么引入此种功能我们先来了解当替换我们自己写的程序时的情况,这里以execl函数为例。说明一下,程序替换可替换在此系统下的所有高级语言程序,即包括python、C/C++、java等。因为所有的语言运行之后都是进程。这里以C/C++为例。

code2.cpp文件

#include <iostream>
#include <cstdio>
using namespace std;
int main(int argc, char* argv[], char* env[])
{
    for (int i = 0; argv[i]; i++)
    {
        fprintf(stdout, "argv[%d]: %s\n", i, argv[i]);
        fprintf(stdout, "env[%d]: %s\n", i, env[i]);  //运行此时的环境变量
    }
    cout << "code2.exe option" << endl;
    return 0;
}


code.cpp文件

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
using namespace std;
int main()
{
    pid_t id  = fork();
    if (id == 0) // 子进程进行进程替换
    {
        cout << "I am a child process, pid = " << getpid() << endl;
        cout << "exec is begining" << endl;
        execl("./code2.exe", "code2.exe", "-a", "-b", NULL); // 运行自己的程序code2.exe
        cout << "exec end" << endl; // execl之后的代码不会运行,因为此时进程被exec替换 
    }
    //父进程执行自己的程序
    int w = wait(NULL);
    if (w > 0)
        cout << "wait success" << endl;
    else 
        cout << "wait failure" << endl;
    return 0;
}


运行code.exe后

[zhu@VM-16-10-centos day2]$ ./code.exe
I am a child process, pid = 29117
exec is begining
argv[0]: code2.exe
env[0]: XDG_SESSION_ID=6732
argv[1]: -a
env[1]: HOSTNAME=VM-16-10-centos
argv[2]: -b
env[2]: TERM=xterm
code2.exe option
wait success

        这里需要说明一下,进程在替换时是不会替换掉环境变量的数据,也就是说以上的程序code2.exe 默认可以通过地址空间继承的方式,让子进程拿到环境变量数据,所以,当我们调用子进程可以输出整个系统的环境变量,因为所有进程都是shell的子进程。但是若是子进程或孙子进程新增环境变量,父进程的进程地址空间中是没有存储的,若父进程想使用子进程新增的环境变量,这时就需要使用 execl* 函数后面带 e 类型的接口,这里以execle为例。

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

envp[]:自定义存储环境变量的指针数组envp[],将此进程自定义的环境变量表覆盖替换程序的环境变量表。

        exec* 后缀加上e的,表示需要传入环境变量表,此时将覆盖原本的环境变量数据。

exec*函数的演示与解说:

[zhu@VM-16-10-centos day2]$ cat code.cpp
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
using namespace std;
int main()
{
    char* env[] = { "AAA=aaa", "BBB=bbb", "CCC=ccc", "DDD=ddd" };
    pid_t id  = fork();
    if (id == 0) // 子进程进行进程替换
    {
        cout << "exec is begining" << endl;
        execle("./code2.exe", "code2x.exe", "-a", "-b", NULL, env); // 执行传入环境变量表的程序进程code2.exe
        cout << "exec end" << endl; // execl之后的代码不会运行,因为此时进程被exec替换 
    }
    //父进程执行自己的程序
    int w = wait(NULL);
    if (w > 0)
        cout << "wait success" << endl;
    else 
        cout << "wait failure" << endl;
    return 0;
}
[zhu@VM-16-10-centos day2]$ cat code2.cpp
#include <iostream>
#include <cstdio>
using namespace std;
int main(int argc, char* argv[], char* env[])
{
    for (int i = 0; env[i]; i++)
    {
        fprintf(stdout, "env[%d]: %s\n", i, env[i]); // 若这里没有传入环境变量表,将输出原有的环境变量,即系统下的环境变量
    }
    cout << "code2.exe option" << endl;
    return 0;
}
[zhu@VM-16-10-centos day2]$ ./code.exe
exec is begining
env[0]: AAA=aaa
env[1]: BBB=bbb
env[2]: CCC=ccc
env[3]: DDD=ddd
env[4]:  
code2.exe option
wait success

       其它exec*接口的类型效果都是一样的,都是根据参数来实现具体的形式。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/466704.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Leetcode 31. 删除无效的括号

心路历程&#xff1a; 一开始看到有点懵&#xff0c;后来发现有点像按照一定规则穷举所有可能情况&#xff0c;想到了排列组合问题&#xff0c;再结合问题长度不固定&#xff0c;无法用已知个for循环表示&#xff0c;从而想到了回溯。这个题相当于需要在一定规则下枚举。 按照…

桌面云解决方案

桌面云解决方案是一种基于云计算的服务&#xff0c;它将用户的桌面环境托管在云端&#xff0c;允许用户通过互联网访问自己的虚拟桌面。这种解决方案为企业和个人用户提供了一种灵活、可扩展且成本效益高的桌面计算方式。以下是一些桌面云解决方案的关键特点和优势&#xff1a;…

MusicHiFi: Fast High-Fidelity Stereo Vocoding

文章目录 abstract abstract demo: https://musichifi.github.io/web/主要用于高精度的音乐场景文章主要做了两件事&#xff1a;&#xff08;1&#xff09;低频mel谱输入&#xff0c;生成更高频率的语音&#xff1b;&#xff08;2&#xff09;单声道音频生成立体声&#xff1b…

AI工具快速部署

演示站见文章底部 部署教程 搭建一键整合包&#xff0c;你需要的东西有&#xff1a; 一个最低1h1g的海外服务器 推荐服务器系统为&#xff1a;CentOS-7.9.2111-x64 一份NineAI一件整合包代码 一定的linux指令知识第一步 通过ssh工具连接服务器 同时打开宝塔面板至文件区域 将…

代码随想录算法训练营第二十五天|● 216.组合总和III ● 17.电话号码的字母组合(JS写法)

216 组合总和Ⅲ 题目链接/文章讲解&#xff1a;https://programmercarl.com/0216.%E7%BB%84%E5%90%88%E6%80%BB%E5%92%8CIII.html 视频讲解&#xff1a;https://www.bilibili.com/video/BV1wg411873x 方法一&#xff1a;自己写的 自己写的&#xff0c;本题和77很像&#xf…

连号区间数c++

题目 输入样例1&#xff1a; 4 3 2 4 1输出样例1&#xff1a; 7输入样例2&#xff1a; 5 3 4 2 5 1输出样例2&#xff1a; 9样例解释 第一个用例中&#xff0c;有 77 个连号区间分别是&#xff1a;[1,1],[1,2],[1,3],[1,4],[2,2],[3,3],[4,4][1,1],[1,2],[1,3],[1,4],[2,2…

关于Oracle Primavera P6的各数据库帐号用途

在使用/维护P6时&#xff0c;经常会用到各种不同的P6数据库用户&#xff0c;如在连接配置P6 Professional时用到的公共帐号pubuser&#xff0c;进入后台维护p6配置信息(adminpv)或开发常连接的privuser&#xff0c;亦或是配置BI Report/BUSINESS Intelligence报表套件用到的pxr…

Axure 中继器的Item属性介绍及使用

item.列名 获取数据行中指定列的值。 index 获取索引编号&#xff0c;编号起始值为1 isFirst 判断数据是否为第一行 isLast 判断是否为最后一行 isEven 判断是否为偶数行 isOdd 判断是否为奇数行 isMarked 判断行是否被标记 isVisible 改行是否为显示

8年测试总结,自动化测试必要注意点+自动化测试框架(汇总)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、开始自动化测试…

工具精灵--超级好用的在线工具网站

工具精灵是一个超级好用的在线工具网站&#xff0c;它有这些功能&#xff1a;json格式化、xml格式化、markdown在线编辑、sql格式化、json转Java、xml转Java等。 虽然有很多这种类似的网站了&#xff0c;但它们并不好用&#xff0c;很粗糙。工具精灵超级好用&#xff0c;细节方…

详解Python中的缩进和选择

缩进 Python最具特色的是用缩进来标明成块的代码。我下面以if选择结构来举例。if后面跟随条件&#xff0c;如果条件成立&#xff0c;则执行归属于if的一个代码块。 先看C语言的表达方式&#xff08;注意&#xff0c;这是C&#xff0c;不是Python!&#xff09; if ( i > 0 …

el-upload的多个文件与单个文件上传

样式图&#xff1a; 场景多个&#xff1a; 使用el-upload上传多个文件 <el-upload class"upload-demo" :action"uploadUrl" :on-remove"handleRemove1":on-success"handleAvatarSuccess1" multiple :limit"5" :on-exc…

gma 2.0.7 (2024.03.16) 更新日志

安装 gma 2.0.7 pip install gma2.0.7网盘下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1P0nmZUPMJaPEmYgixoL2QQ?pwd1pc8 提取码&#xff1a;1pc8 注意&#xff1a;此版本没有Linux版&#xff01; 编译gma的Linux虚拟机没有时间修复&#xff0c;本期Linux版继…

阿里云服务器1个月收费价格表,5元1个月起

阿里云服务器一个月多少钱&#xff1f;最便宜5元1个月。阿里云轻量应用服务器2核2G3M配置61元一年&#xff0c;折合5元一个月&#xff0c;2核4G服务器30元3个月&#xff0c;2核2G3M带宽服务器99元12个月&#xff0c;轻量应用服务器2核4G4M带宽165元12个月&#xff0c;4核16G服务…

现货白银是伦敦银吗?要看这个因素

在国际贵金属投资市场上&#xff0c;伦敦银就是现货白银交易的别名&#xff0c;但这仅仅是指以“美元/盎司”计价、在全球化的网络进行的正宗国际现货白银交易&#xff0c;一些个别国家和地区的现货白银交易&#xff0c;可不能称为伦敦银交易。 伦敦作为全球金融中心之一&#…

Java微服务分布式事务框架seata

&#x1f339;作者主页&#xff1a;青花锁 &#x1f339;简介&#xff1a;Java领域优质创作者&#x1f3c6;、Java微服务架构公号作者&#x1f604; &#x1f339;简历模板、学习资料、面试题库、技术互助 &#x1f339;文末获取联系方式 &#x1f4dd; 往期热门专栏回顾 专栏…

深度强化深化03 Rewards and Returns

Return Value Function Vpei当前的局势好不好 自动驾shi方向盘的角度

AIGC元年大模型发展现状手册

零、AIGC大模型概览 AIGC大模型在人工智能领域取得了重大突破&#xff0c;涵盖了LLM大模型、多模态大模型、图像生成大模型以及视频生成大模型等四种类型。这些模型不仅拓宽了人工智能的应用范围&#xff0c;也提升了其处理复杂任务的能力。a.) LLM大模型通过深度学习和自然语…

【Python循环3/5】条件循环语句

目录 导入 条件循环 边界条件 while循环 死循环 while循环与for循环的区别 总结 知识图谱 导入 我们已经学习了如何利用for语句实现代码重复执行的循环结构。通过遍历列表&#xff0c;输出其中的每一个元素。 for循环就像是排队办事&#xff0c;一个个进入&#xff0c;轮…

一个注解解决接口耗时日志的打印

在日常开发中&#xff0c;常常需要统计方法的耗时情况&#xff0c;一般的写法是在进入方法之前&#xff0c;记录一下当前时间戳&#xff0c;在方法最后再用当前时间戳减去进入时候的时间戳就是耗时情况&#xff0c;方法很简单&#xff0c;但不够优雅。 接下来我们用一个注解AOP…