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

news2024/11/19 3:33:56

 介绍:

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

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

替换原理

        通常,用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*接口的类型效果都是一样的,都是根据参数来实现具体的形式。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

鸿蒙Harmony应用开发—ArkTS声明式开发(容器组件:FlowItem)

瀑布流组件的子组件&#xff0c;用来展示瀑布流具体item。 说明&#xff1a; 该组件从API Version 9开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。仅支持作为Waterflow组件的子组件使用。 子组件 支持单个子组件。 接口 FlowItem() 使…

【RK android6.0 实现假关机真开机效果】

RK android6.0 实现假关机真开机效果 需求描述解决方法 郑重声明:本人原创博文&#xff0c;都是实战&#xff0c;均经过实际项目验证出货的 转载请标明出处:攻城狮2015 Platform: Rockchip CPU:rk3368 OS:Android 7.1.2 Kernel: 3.10 需求描述 由于硬件设计&#xff0c;使用错误…

Qt实现简单的五子棋程序

Qt五子棋小程序 Qt五子棋演示及源码链接登陆界面单机模式联机模式联网模式参考 Qt五子棋 参考大佬中国象棋程序&#xff0c;使用Qt实现了一个简单的五子棋小程序&#xff0c;包含了单机、联机以及联网三种模式&#xff1b;单机模式下实现了简易的AI&#xff1b;联机模式为PtoP…

Verilog case/casez/casex的区别

casez/casex语句虽然EDA工具也可以综合出来&#xff0c;但是注意&#xff0c;casez/casex综合出来的电路和case语句综合出来的电路可能是不同的&#xff0c;一定要慎用。而且综合工具也会告诉你casez/casex中的“?”"x""z"的comparison is always false&a…

提速增效!Figma插件推荐,助你事半功倍!

随着设计工具的更换&#xff0c;设计师不再局限于传统软件的重复操作&#xff0c;而是越来越追求能够提高设计效率的插件。从Photoshop到Sketch&#xff0c;再到最受欢迎的Figma&#xff0c;插件层出不穷。Figma是一种基于浏览器和团队合作的设计工具&#xff0c;可以在任何平台…

HackTheBox Blackfield

[1] 靶机信息状态退役难度HardIP/地址https://app.hackthebox.com/machines/Blackfield价格需要订阅 14$ /20 $ /月 端口扫描 └──╼ #nmap -p- --min-rate1000 -T4 10.129.229.17 Starting Nmap 7.93 ( https://nmap.org ) at 2024-03-14 14:14 GMT Nmap scan report for…

【iOS】ARC学习

文章目录 前言一、autorelease实现二、苹果的实现三、内存管理的思考方式__strong修饰符取得非自己生成并持有的对象__strong 修饰符的变量之间可以相互赋值类的成员变量也可以使用strong修饰 __weak修饰符循环引用 __unsafe_unretained修饰符什么时候使用__unsafe_unretained …

JVM中对象创建过程

在JVM中对象的创建&#xff0c;我们从一个new指令开始&#xff1a; 这个过程大概图示如下&#xff1a; 虚拟机收到new指令触发。 类加载检查&#xff1a;如果类没有被类加载器加载&#xff0c;则执行类加载流程&#xff08;将class信息加载到JVM的运行时数据区的过程&#xff…

【SRE系列之docker容器】--dockerfile镜像优化

dockerfile镜像优化 1.1 镜像优化方法 系统镜像采用ubuntu或者alpine&#xff0c;会比centos少1G左右编写业务镜像时从官网拉取镜像&#xff0c;其余配置根据业务需求再配置编写dockerfile时把不用的安装包卸载或者删除尽量减少run命令的使用&#xff08;一个run命令&#xf…

《手把手教你》系列技巧篇(三十九)-java+ selenium自动化测试-JavaScript的调用执行-上篇(详解教程)

1.简介 在做web自动化时&#xff0c;有些情况selenium的api无法完成&#xff0c;需要通过第三方手段比如js来完成实现&#xff0c;比如去改变某些元素对象的属性或者进行一些特殊的操作&#xff0c;本文将来讲解怎样来调用JavaScript完成特殊操作。 2.用法 创建一个执行 JS 的…

公众号关闭自定义菜单

1、登录公众号 https://mp.weixin.qq.com/ 2、找到侧边导航-》新的功能 3、已开通-》自定义菜单 4、点击停用

Cesium:绘制一个 3DTiles 对象的外包盒顶点

作者:CSDN @ _乐多_ 本文将介绍如何使用 Cesium 引擎根据模型的中心坐标,半轴信息,绘制一个 3DTiles 对象的外包盒顶点。 外包盒是一个定向包围盒(Oriented Bounding Box),它由一个中心点(center)和一个包含半轴(halfAxes)组成。半轴由一个3x3的矩阵表示,这个矩阵…

产品数据管理系统哪家好?产品数据管理系统厂商

产品数据管理系统&#xff08;PDM&#xff09;的选择取决于企业的具体需求、规模、行业以及预算。市场上有很多优秀的PDM供应商&#xff0c;每一家都有其独特的优势和特点。以下是一些在市场上广受好评的PDM供应商&#xff0c;供您参考&#xff1a; 彩虹PLM系统&#xff1a;彩虹…

java垃圾回收-三色标记法

三色标记法 引言什么是三色标记法白色灰色黑色 三色标记过程三色标记带来的问题多标问题漏标问题 如何弥补漏标问题增量更新原始快照总结 引言 在CMS,G1这种并发的垃圾收集器收集对象时&#xff0c;假如一个对象A被GC线程标记为不可达对象&#xff0c;但是用户线程又把A对象做…

GEE:基于变异系数法(CV)进行遥感生态指数(RSEI)波动分析

作者:CSDN @ _养乐多_ 本文将在 Google Earth Engine(GEE)平台上复现论文《基于遥感生态指数的青藏公路典型路段路域生态环境质量评估与分析》中使用变异系数法对遥感生态指数(RSEI)进行波动分析的方法和代码。 其公式如下所示, 结果如下所示, 文章目录 一、核心函数二…

c语言(数据在内存中的存储)

1. 整数在内存中的存储 整数的2进制表⽰⽅法有三种&#xff0c;即原码、反码和补码 三种表⽰⽅法均有符号位和数值位两部分&#xff0c;符号位都是⽤0表⽰“正”&#xff0c;⽤1表⽰“负”&#xff0c;⽽数值位最 ⾼位的⼀位是被当做符号位&#xff0c;剩余的都是数值位。 正整…

osgEarth学习笔记1-安装osgEarth开发环境

原文链接 本文主要是为了防止丢失&#xff0c;做一些记录&#xff0c;仅供个人学习使用。 QGis的学习和使用基本告一段落了。日常的应用已经离不开QGis了&#xff0c;常用的QGis-API和跨平台的QTQGis开发已经十分熟练了。涉及遥感和GIS领域的二维可视化、数据处理使用QT搭配Q…

Python下有关CV的一些算法和函数

目录&#xff1a; 1. HoughCircles二级目录三级目录 1. HoughCircles 霍夫圆检测 二级目录 三级目录

Linux系统部署DolphinScheduler任务调度系统并实现无公网IP远程访问

文章目录 前言1. 安装部署DolphinScheduler1.1 启动服务 2. 登录DolphinScheduler界面3. 安装内网穿透工具4. 配置Dolphin Scheduler公网地址5. 固定DolphinScheduler公网地址 前言 本篇教程和大家分享一下DolphinScheduler的安装部署及如何实现公网远程访问&#xff0c;结合内…