【Linux修行路】进程控制——程序替换

news2024/11/15 3:44:37

目录

⛳️推荐

一、单进程版程序替换看现象

二、程序替换的基本原理

三、程序替换接口学习

3.1 替换自己写的可执行程序

3.2 第三个参数 envp 验证


⛳️推荐

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站

一、单进程版程序替换看现象

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

int main()
{
    printf("befor: I am a process, pid:%d, ppid:%d\n", getpid(), getppid());

    //exec类函数的标准写法
    execl("/usr/bin/ls", "ls", "-a", "-l", NULL);
    
    printf("after: I am a process, pid:%d, ppid:%d\n", getpid(), getppid());
    return 0;
}

在调用了程序替换函数 execl 后,我们的进程去执行了 ls 指令,并且原进程的 after 信息没有打印。在前面的文章中讲过,指令本质上就是可执行程序。因此程序替换函数 execl 可以用其它进程来替换当前进程。

二、程序替换的基本原理

当进程调用一种 exec 函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用 exec 并不创建新进程,所以调用 exec 前后该进程的 pid 并未改变。

补充

  • 程序替换成功后,exec 系列函数后续的代码不会被执行,只有替换失败才有可能执行后续代码。exec 系列函数,只有失败返回,没有成功返回。

  • Linux 中形成的可执行程序本质上是 EFL 格式的文件,该文件有一个表头,里面保存了该可执行程序的入口地址。

三、程序替换接口学习

和程序替换有关的接口一共有七种,其中一个是系统调用,剩下六个都是库函数。

  • 系统调用:
#include <unistd.h>
int execve(const char *filename, char *const argv[], char *const envp[]);
  • 库函数
#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 execvpe(const char *file, char *const argv[], char *const envp[]);

上面这六种函数的参数主要是为了解决以下两个问题:如何找到新替换进来的程序?如何执行新替换的程序。因此:

第一个参数就是解决第一问题的,有两种解决方案,第一种是函数名不带 p 的,此时第一个形参 path 表示的是可执行程序的全路径;第二种是函数名代 p 的,此时第一个形参 file 表示的是可执行程序的名字,函数会拿着这个名字去 PATH 环境变量下搜索这个可执行程序。

第二个参数就是解决第二个问题的,也有两种解决方案。如何执行新替换的可执行程序本质上就是要知道执行该程序的指令以及参数是什么。l 表示参数采用列表,以可变参数的形式将指令和选项传进去(命令行中输入什么,这里就传什么),最后要以 NULL 结尾。v 表示采用字符串指针数组的方式,把指令以及选项都存在一个字符串指针数组中(最后必须是 NULL),然后把这个数组作为实参传给程序替换函数。这个参数最终会作为命令行参数传递给新替换进来的可执行程序。

第三个参数 envp,如果使用这个参数,那么新替换进来的进程将采用覆盖的策略彻底替换掉父进程的环境变量,即使用该参数后,新替换进来的进程不再继承父进程的环境变量,而是完全以 envp 数组中的内容作为自己的环境变量。

小Tips:在将进程地址空间的时候画过一张关于进程地址空间的图,其中有一部分存储的就是命令行参数和环境变量,而子进程的进程地址空间是继承自父进程的,环境变量也是在这个时候继承下去的,因此一个进程的环境变量在该进程创建的时候就已经被该进程从父进程那里继承下来了,在程序替换的过程中,环境变量信息不会被替换

// 首先在 bash 中加入自定义的环境变量
export MY_VALUE=123456
// mycommand.c
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <errno.h>

int main(int argc, char* argv[], char* env[])
{
    printf("befor: pid: %d, ppid: %d, mycommand running...\n", getpid(), getppid());
    printf("mycommand get MY_VALUE: %s\n", getenv("MY_VALUE"));
    pid_t id = fork();
    if (id == 0)
    {
        // child
        sleep(2);
        // exec类函数的标准写法
        char* const child_argv[] = {"otherexe", "-a", "-b", "-c", NULL};
        execv("./otherexe", child_argv);// 替换成otherexe程序
        printf("%s\n", strerror(errno));
        exit(errno);// 如果执行了这段代码说明程序替换失败
    }
    // father
    sleep(4);
    pid_t ret = waitpid(id, NULL, 0);
    if(ret > 0)
    {
        printf("wait success, my pid: %d, ret pid: %d\n", getpid(), ret);
    }
    return 0;
}
#include <iostream>
#include <unistd.h>
#include <stdlib.h>

using namespace std;

int main(int argc, char* argv[], char* env[])
{
    printf("befor: pid: %d, ppid: %d, otherexe running...\n", getpid(), getppid());
    printf("otherexe get MY_VALUE: %s\n", getenv("MY_VALUE"));
    return 0;
}

分析:首先在 bash 中添加环境变量 MY_VALUE,mycommand 程序作为 bash 的子进程一定会继承该环境变量,在 mycommand 先创建一个子进程,它也会继承 mycommand 的环境变量,然后在子进程中调用 execv 接口进程程序替换,将 otherexe 程序替换进来,在 otherexe 程序中我们调用 getenv 接口,仍然可以获得 MY_VALUE 这个环境变量的值,这证明了我们上面说的:在进行程序替换的过程中,环境变量信息不会被替换。补充一点,如果要在当前进程的地址空间中新增环境变量可以调用 putenv 接口。

小结:在 bash 中执行的所有程序其实都是子进程,并且都是通过 exec 系列函数将程序对应的代码和数据加载到内存中。因此 exev 系列函数起到加载器的效果,函数里面一定会涉及到内存申请、外设访问等动作。

3.1 替换自己写的可执行程序

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

int main()
{
    char* const argv[] = {"ls", "-a", "-l", NULL};
    printf("befor: I am a process, pid: %d, ppid: %d\n", getpid(), getppid());
    pid_t id = fork();
    if (id == 0)
    {
        // child
        sleep(2);
        // exec类函数的标准写法
        execl("./otherexe", "otherexe", NULL);
        printf("%s\n", strerror(errno));
        exit(errno);// 如果执行了这段代码说明程序替换失败
    }
    // father
    sleep(6);
    pid_t ret = waitpid(id, NULL, 0);
    if(ret > 0)
    {
        printf("wait success, my pid: %d, ret pid: %d\n", getpid(), ret);
    }
    return 0;
}
// otherexe.cc
#include <iostream>

using namespace std;

int main()
{
    cout << "Hello Linux!" << endl;
    return 0;
}

代码解释:其中 otherexe 是我们自己写的一个 C++ 程序,在 mycommand 程序中先创建一个子进程,然后在该子进程中调用程序替换函数 execl,将我们自己写的 mycommand 程序替换进来。这里有一个细节就是 execl 的第二参数到底传 ./otherexe 还是 otherexe,因为上面说过在 bash 中如何输入这个参数就怎么传,在 bash 中执行 otherexe 程序输入的指令是:./otherexe,其中 ./ 主要是为了告诉 bash otherexe 这个程序就在当前目录下。而 execl 函数的第一个参数就已经将 otherexe 程序的详细路径传进去了,所以第二个参数可以直接传 otherexe。但经过验证传 othe

rexe 和 ./otherexe 都可以。

3.2 第三个参数 envp 验证

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

int main(int argc, char* argv[], char* env[])
{
    printf("befor: pid: %d, ppid: %d, mycommand running...\n", getpid(), getppid());
    pid_t id = fork();
    if (id == 0)
    {
        // child
        sleep(2);
        // exec类函数的标准写法
        char* const child_argv[] = {"otherexe", "-a", "-b", "-c", NULL};
        char* const myenv[] = {
            "MY_PATH=./usr/wcy/linux-s",
            "MY_VALUE=asdfg",
            "NAME=wcy",
            NULL
        };// 自定义环境变量
        execle("./otherexe", "otherexe", "-a", "-w", "-z", NULL, myenv);// 替换成otherexe程序
        printf("%s\n", strerror(errno));
        exit(errno);// 如果执行了这段代码说明程序替换失败
    }
    // father
    sleep(4);
    pid_t ret = waitpid(id, NULL, 0);
    if(ret > 0)
    {
        printf("wait success, my pid: %d, ret pid: %d\n", getpid(), ret);
    }
    return 0;
}
// otherexe.cc
#include <iostream>
#include <unistd.h>
#include <stdlib.h>

using namespace std;

int main(int argc, char* argv[], char* env[])
{
    printf("befor: pid: %d, ppid: %d, otherexe running...\n", getpid(), getppid());
    cout << "命令行参数:" << endl;
    for(int i = 0; argv[i]; i++)
    {
        cout << i << ": " << argv[i] << endl;
    }
    cout << "我是环境变量:" << endl;
    for(int i = 0; env[i]; i++)
    {
        cout << i << ": " << env[i] << endl;
    }
    return 0;
}

🎁结语:

        今天的分享到这里就结束啦!如果觉得文章还不错的话,可以三连支持一下,您的支持就是我前进的动力!

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

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

相关文章

Dify 开源大语言模型(LLM) 应用开发平台如何使用Docker部署与远程访问

目录 ⛳️推荐 前言 1. Docker部署Dify 2. 本地访问Dify 3. Ubuntu安装Cpolar 4. 配置公网地址 5. 远程访问 6. 固定Cpolar公网地址 7. 固定地址访问 ⛳️推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享…

Vue3列表(List)

效果如下图&#xff1a;在线预览 APIs List 参数说明类型默认值bordered是否展示边框booleanfalsevertical是否使用竖直样式booleanfalsesplit是否展示分割线booleantruesize列表尺寸‘small’ | ‘middle’ | ‘large’‘middle’loading是否加载中booleanfalsehoverable是否…

mysql写个分区表

因为表量已经达到1个亿了。现在想做个优化&#xff0c;先按照 create_time 时间进行分区吧。 create_time 是varchar类型。 CREATE TABLE orders (id varchar(40) NOT NULL ,order_no VARCHAR(20) NOT NULL,create_time VARCHAR(20) NOT NULL,amount DECIMAL(10,2) NOT NULL,…

Unity如何使用Spine动画导出的动画

Unity如何使用Spine动画导出的动画 介绍使用版本Spine导出源文件修改Spine3.8.75版本导入Unity的3.8版本Spine的报错Unity辅助修改Json中版本号方式总结 介绍 最近公司在做抖音小程序的小游戏&#xff0c;我们这边动画部分使用的是spine动画&#xff0c;所以会有spine导入的问…

IDEA使用LiveTemplate快速生成方法注释

本文目标&#xff1a;开发人员&#xff0c;在了解利用Live Template动态获取方法输入输出参数、创建日期时间方法的条件下&#xff0c;进行自动生成方法注释&#xff0c;达到自动添加方法注释的程度&#xff1b; 文章目录 1 场景2 要点2.1 新增LiveTemplate模版2.2 模版内容填写…

FFMPEG推流器讲解

FFMPEG重要结构体的讲解 FFMPEG中有六个比较重要的结构体&#xff0c;分别是AVFormatContext、AVOutputFormat、 AVStream、AVCodec、AVCodecContext、AVPacket、AVFrame、AVIOContext结构体&#xff0c;这几个结构体是贯穿着整个FFMPEG核心功能。 AVFormatContext 这个结构…

基于web的大学生一体化服务平台的设计与实现

TOC springboot209基于web的大学生一体化服务平台的设计与实现 第1章 绪论 1.1 课题背景 二十一世纪互联网的出现&#xff0c;改变了几千年以来人们的生活&#xff0c;不仅仅是生活物资的丰富&#xff0c;还有精神层次的丰富。在互联网诞生之前&#xff0c;地域位置往往是人…

1、.Net UI框架:WinUI - .Net宣传系列文章

WinUI(Windows UI Library)是微软提供的一个用于构建Windows应用程序的本机UI平台组件。它与Windows应用SDK紧密相关&#xff0c;允许开发者创建适用于Windows 10及更高版本的应用程序&#xff0c;并且可以发布到Microsoft Store。WinUI 3是最新的一代&#xff0c;它提供了与操…

C# 中 Tuple 与 ValueTuples 之间的区别

在 C# 中&#xff0c;元组和值元组都用于在单个变量中存储多个值。但它们在语法、功能和性能方面存在一些关键差异。 一.Tuples(元组) 元组是一种引用类型&#xff0c;长期以来一直是 .NET 的一部分。它们是使用 System.Tuple 类创建的。 例子 using System; class Program…

养猫家庭必备好物?希喂、霍尼韦尔宠物空气净化器真实测评

随着宠物空气净化器的讨论度越来越高&#xff0c;我也被种草了这款产品。对养宠家庭来说&#xff0c;十分需要这样一款转专门针对宠物毛发清理的工具。准备入手前我在网上做了许多功课&#xff0c;经过一番筛选后&#xff0c;最后希喂、霍尼韦尔两个品牌成功晋级决赛。 在对比…

代理IP为什么不能使用免费代理IP地址?

在跨境业务中&#xff0c;营销人员、广告投手经常利用代理IP防止账号关联与封禁&#xff0c;并且在访问网站时可以隐匿真实 IP 地址&#xff0c;定位目标市场。代理服务器充当中间人掩盖真实的数字足迹&#xff0c;这不仅增强了隐私&#xff0c;也会跨境业务提效提供保障。但是…

基于STM32开发的智能语音助手系统

目录 引言环境准备工作 硬件准备软件安装与配置系统设计 系统架构硬件连接代码实现 初始化代码控制代码应用场景 智能家居控制个人语音助理常见问题及解决方案 常见问题解决方案结论 1. 引言 随着人工智能技术的发展&#xff0c;智能语音助手已经逐渐进入了人们的日常生活。…

【轨物推荐】创新有规律,发明有方法

原创 赵敏 发明方法研究 2020年03月11日 10:38 各位业内朋友&#xff0c;大家好&#xff01; 今年的疫情&#xff0c;对所有的企业都会有冲击&#xff0c;给企业的业务开展带来很多困扰。详细很多企业都在出主意、想办法&#xff0c;设法把疫情造成的损失降到最低。即使在平时…

行业原型:智慧制造:注塑云管工厂

行业原型预览链接&#xff1a;&#xff08;请与班主任联系获取原型文档&#xff09; 文件类型&#xff1a;.rp 支持版本&#xff1a;Axrure RP 8 文档名称&#xff1a;智慧制造&#xff1a;注塑云管工厂 文件大小&#xff1a;1.80 MB 目录内容介绍 文档内容介绍 回复 “211…

均数(mean±SD)与RR/OR值可以合并进行Meta分析吗?

经常有小伙伴问&#xff1a;在做危险因素的Meta分析时&#xff0c;遇到一些文献比较的是病例组和对照组某一指标的均值差异&#xff0c;数据以meanSD形式呈现&#xff0c;而另一些文献则是以OR或RR (95%CI)的形式描述该指标与疾病的关联。这两种数据形式可以一起进行Meta分析吗…

每周心赏|用AI真的可以开挂式求职

马上就要到2024年的金九银十了&#xff0c;正是求职的好时机啊&#xff01; 万事开头难&#xff0c;找工作第一步自然是离不开制作简历了&#xff01; 都说简历是面试的敲门砖&#xff0c;所以大家都很重视。 你不是也曾游走在各大网站中搜寻模板、寻找“大能们”的简历来修…

海运如何实时了解货物的物流轨迹?有什么系统可以实现?

物流轨迹在散货集拼业务中扮演着至关重要的角色&#xff0c;它不仅让客户可以实时掌握货物动态&#xff0c;确保了货物的安全无虞与准时送达&#xff0c;还为企业提供了灵活调整运输策略的依据&#xff0c;有益于运输效率与可靠性的双重提升。同时&#xff0c;通过物流轨迹的即…

RC电路里,电容多久可以充满电

时间常数 τR*C&#xff0c;那么电容需要多久能充满电呢&#xff1f; 例如下图仿真&#xff0c;R1KΩ&#xff0c;C1uF&#xff0c;那么τR*C1ms。 2个时间常数2ms的时间&#xff0c;电容电压充到86% 3个时间常数3ms的时间&#xff0c;电容电压充到95% 通常定义95为充满电。…

前端实现视频流播放:封装一个可复用的HlsPlayer组件

简介 在前端开发中&#xff0c;播放视频流是一个常见的需求&#xff0c;尤其是在需要实时监控或直播的场景中。本文将分享如何封装一个基于hls.js库的Vue组件&#xff0c;以便在任何需要的地方快速引用和播放视频流。 环境准备 首先&#xff0c;确保你的项目中已经安装了Vue…

【整数规划】+【0—1规划】解决优化类问题(Matlab代码)

目录 文章目录 前言 一、整数规划 分类&#xff1a; 二、典例讲解 1.背包问题 2.指派问题 总结 前言 如果觉得本篇文章还不错的话&#xff0c;给作者点个赞鼓励一下吧&#x1f601;&#x1f601;&#x1f601; 在规划问题中&#xff0c;有些最优解可能是分数或小数&am…