当前位置: 首页 > news >正文

【Linux】:程序替换

 朋友们、伙计们,我们又见面了,本期来给大家解读一下有关Linux程序替换的相关知识点,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成!

C 语 言 专 栏:C语言:从入门到精通

数据结构专栏:数据结构

个  人  主  页 :stackY、

C + + 专 栏   :C++

Linux 专 栏  :Linux

目录

1. 程序替换

2. 单进程的程序替换

2.1 单进程程序替换的原理

3. 多进程的程序替换

3.1 多进程程序替换原理

4. 剩余接口的介绍以及使用 

4.1 execl函数介绍

4.2 execlp函数介绍

4.3 execv函数介绍 

4.4 execvp函数介绍

4.5 替换别的程序 

5. 环境变量和程序替换

5.1 从bash开始继承 

5.2 从父进程开始继承 

5.3 环境变量默认继承

5.4 execle函数介绍 

5.5 execve系统调用 


1. 程序替换

我们创建的所有子进程,执行的代码都是父进程代码的一部分!那么如果我们想让子进程执行新的程序呢?执行全新的代码和访问全新的数据,不再和父进程有所瓜葛,那么就需要用到程序替换。

这么多程序替换的函数调用,到底该怎么样用呢?我们直接开始使用:

2. 单进程的程序替换

为了便于理解,我们先从单进程的程序替换开始:

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

先来看最简单的程序替换接口,其中里面的三个点表示的意思就是可变参数,使用的方法和我们的printf函数中的可变参数一样;

我们程序要能运行起来,第一步先要找到这个程序,第二部如何运行。

  • const char* path表示:新程序的文件路径 + 文件名;
  • const char *arg表示:这个程序怎么运行(简单的说我们在命令行怎么输就怎么写,最终以NULL结尾)。

返回值:成功没有返回值,失败返回-1。

我们先来演示一遍:让ls命令替换掉我们写的可执行

#include <stdio.h>
#include <unistd.h>int main()
{printf("pid: %d, exec command begin\n", getpid());sleep(3);// 程序替换execl("/usr/bin/ls", "ls", "-a", "-l", NULL);printf("pid: %d, exec command begin\n", getpid());return 0;
}

通过结果可以发现,我们通过execl成功完成了程序替换,但是代码中的最后一行代码为什么没有打印出来呢?这个到后续再解释!

2.1 单进程程序替换的原理

我们已经完成了单进程的程序替换,那么它的原理是什么呢?

当一个进程运行起来时,OS会为它创建PCB、虚拟地址空间、页表,将它的代码、数据加载到物理内存中,并且然后通过页表建立起虚拟到物理的映射关系;当进行程序替换时,execl就将要替换的程序代码和数据直接在物理内存中替换覆盖掉,此时原来的代码和数据就会被新替换的代码和数据覆盖掉,继续向后执行也就执行的新程序的代码和数据,这个过程中并没有创建新的进程。

3. 多进程的程序替换

见识过单进程的程序替换以及程序替换的原理,接下来替换一下多进程:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{pid_t id = fork();if(id == 0){// childprintf("pid: %d, exec command begin\n", getpid());sleep(3);// 程序替换execl("/usr/bin/ls", "ls", "-a", "-l", NULL);printf("pid: %d, exec command begin\n", getpid());}// parentpid_t rid = waitpid(id, NULL, 0);if(rid == id){printf("wait success, rid: %d\n", rid);}return 0;
}

3.1 多进程程序替换原理

首先,进程具有独立性,其次我们都知道,父子进程代码共享,数据以写时拷贝的方式各自私有一份,那么在程序替换的时候,把子进程的数据替换这没问题,因为父子数据各自私有,但是将代码替换,这就有点不能忍了,父子进程代码共享,既然子进程都执行的是替换后的代码,为什么父进程在程序替换的时候还是继续执行他自己代码呢?

是因为在多进程的程序替换时,当子进程启动程序替换覆盖它们的共享代码时,也会发生写时拷贝的方式,重新开辟一块空间,然后进行替换写入,这样父子进程的代码就不共享,子进程继续执行它替换后的代码,父进程继续执行自己的代码。

在替换完之后,子进程怎么知道从新程序的哪里开始执行呢?

我们的代码在编译形成可执行程序的时候会有一个程序入口地址--entry地址;

CPU内部的eip寄存器可以记录我们程序当前运行到哪里了,所以当程序替换之后,新程序的eip程序计数器就被修改为entry地址,所以就可以重新开始运行了。 

接下来解决上面遗留的问题: 代码中的最后一行代码为什么没有打印出来呢?

是因为在程序替换以后,代码和数据都被覆盖了,所以execl函数替换成功后,后续的代码没有机会执行了,因为已经被替换掉了!

所以我们不用判断它的返回值,如果继续执行了后续代码,说明程序替换出错了。

4. 剩余接口的介绍以及使用 

要完成程序替换需要满足以下两点:

  • 1. 必须先要找到需要替换的程序。
  • 2. 必须告诉替换函数该怎么执行。

这些程序替换的函数前面的单词都相同,主要是后缀不同,代表的意思也不同。

4.1 execl函数介绍

execl中的l表示列表的意思,在我们传递使用方法的时候,就像一个列表一样,最后以NULL结尾。也就类似于一个list链表一样。

4.2 execlp函数介绍

execlp中的p表示要找的目标可执行程序会自动在环境变量PATH中根据file去查找。

execlp("ls", "ls", "-a", "-l", NULL);
// 两个ls表示的含义不一样
// 第一个表示要执行的文件
// 第二个表示如何执行

4.3 execv函数介绍 

execv中v表示的是数组传参的意思,可以将如何执行保存一个数组中,然后传递该数组即可。

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{pid_t id = fork();if(id == 0){// childprintf("pid: %d, exec command begin\n", getpid());sleep(3);// 程序替换char *const argv[] = {"ls","-a","-l",NULL};execv("/usr/bin/ls", argv);printf("pid: %d, exec command begin\n", getpid());}// parentpid_t rid = waitpid(id, NULL, 0);if(rid == id){printf("wait success, rid: %d\n", rid);}return 0;
}

4.4 execvp函数介绍

execvp就是execv和execlp和结合,可以直接传递文件名,并且执行方式以数组传参的方式传递。

        char *const argv[] = {"ls","-a","-l",NULL};execvp("ls", argv);//execvp("argv[0]", argv);

4.5 替换别的程序 

程序替换不仅可以替换系统的命令,还可以替换我们自己写的任何程序:

用C++程序来替换C语言程序

#include <iostream>int main()
{std::cout << "hello C++" << std::endl;std::cout << "hello C++" << std::endl;std::cout << "hello C++" << std::endl;std::cout << "hello C++" << std::endl;return 0;
}
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{pid_t id = fork();if(id == 0){// childprintf("pid: %d, exec command begin\n", getpid());sleep(3);// 程序替换execl("./test", "test", NULL);printf("pid: %d, exec command begin\n", getpid());}// parentpid_t rid = waitpid(id, NULL, 0);if(rid == id){printf("wait success, rid: %d\n", rid);}return 0;
}

我们在命令行执行可执行程序时用的是./exe,那么为什么在传参时只传递exe,因为./表示的意思是先找到该程序,在运行,execl第一个参数已经找到了,所以只需要传递exe即可。

5. 环境变量和程序替换

在环境变量章节说过环境变量具有全局属性,那么当我们进行程序替换的时候,子进程对应的环境变量是可以直接从父进程中来的,父进程的环境变量从bash中来,我们可以通过代码来验证一下:

5.1 从bash开始继承 

同样的我们还是使用程序替换的方式,来看看子进程替换之后从bash继承下来的环境变量。

先在命令行自定义一些环境变量:

export MYENV1=1111111111111
export MYENV2=2222222222222

源test.cc

#include <iostream>int main(int argc, char *argv[], char *env[])
{int i = 0;for(; env[i]; i++){std::cout << env[i] << std::endl;  // 打印环境变量}return 0;
}

源myprocess.c

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{pid_t id = fork();if(id == 0){printf("pid: %d, exec command begin\n", getpid());sleep(1);execl("./test", "test", NULL);printf("pid: %d, exec command begin\n", getpid());}pid_t rid = waitpid(id, NULL, 0);if(rid == id)printf("wait success, rid: %d\n", rid);return 0;
}

可以看到父进程继承了bash的环境变量,子进程继承了父进程的环境变量。

5.2 从父进程开始继承 

在程序中导入环境变量的接口为:putenv();

#include <stdlib.h>
int putenv(char *string);

在父进程中导入一个环境变量,看看子进程能否继承:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>int main()
{char *env_val = "MYVAL5=5555555555555555555555555";putenv(env_val);pid_t id = fork();if(id == 0){printf("pid: %d, exec command begin\n", getpid());sleep(1);execl("./test", "test", NULL);printf("pid: %d, exec command begin\n", getpid());}pid_t rid = waitpid(id, NULL, 0);if(rid == id)printf("wait success, rid: %d\n", rid);return 0;
}

 

5.3 环境变量默认继承

在进程地址空间这一章节我们了解到了进程地址空间中有环境变量和命令行参数,在父进程创建子进程的过程中,子进程以父进程为模版,子进程的地址空间也就以父进程的地址空间为模版创建了,进而父进程的环境变量和命令行参数也就都会被继承下来,所以通过地址空间可以让子进程继承父进程的环境变量和数据,所以说这是一种默认的行为。

那么继承下去的环境变量为什么不会收到程序替换的影响呢?环境变量也是数据呀!

在程序替换的过程中,只会替换程序的代码和数据,并不会对环境变量产生影响。

5.4 execle函数介绍 

将父进程的环境变量传递给子进程有两种方式:

1. 直接使用(默认集成,需要传)。

2. 使用execle函数的第三个参数直接传递。

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>extern char ** environ; int main()
{pid_t id = fork();if(id == 0){printf("pid: %d, exec command begin\n", getpid());sleep(1);execle("./test", "test",NULL, environ);  // 直接传printf("pid: %d, exec command begin\n", getpid());}pid_t rid = waitpid(id, NULL, 0);if(rid == id)printf("wait success, rid: %d\n", rid);return 0;
}

如果要传递我们自己的环境变量,可以先定义,再传递;同样我们也可以把命令行参数传递给子进程,然后通过程序替换来展现出来:

源test.cc

#include <iostream>
#include <unistd.h>int main(int argc, char *argv[], char *env[])
{for(int i = 0; i < argc ; i++){std::cout << i << "->" <<  argv[i] << std::endl;}std::cout << "################################" << std::endl;for(int i = 0; environ[i]; i++){std::cout << i << " : " << env[i] << std::endl;}return 0;
}

源process.c

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>int main()
{char *const myenv[] = {"MYVAL1=11111111111111","MYVAL2=22222222222222","MYVAL3=33333333333333","MYVAL4=44444444444444",NULL};pid_t id = fork();if (id == 0){printf("pid: %d, exec command begin\n", getpid());sleep(1);execle("./test", "test", "-a", "-b", NULL, myenv); // 直接传printf("pid: %d, exec command begin\n", getpid());}pid_t rid = waitpid(id, NULL, 0);if (rid == id)printf("wait success, rid: %d\n", rid);return 0;
}

可以看到传递的环境变量不是新增,而是覆盖是传递!

程序替换可以将命令行参数、环境变量通过自己的参数,传递给被替换程序的mian函数中!

5.5 execve系统调用 

朋友们、伙计们,美好的时光总是短暂的,我们本期的的分享就到此结束,欲知后事如何,请听下回分解~,最后看完别忘了留下你们弥足珍贵的三连喔,感谢大家的支持!      

相关文章:

【Linux】:程序替换

朋友们、伙计们&#xff0c;我们又见面了&#xff0c;本期来给大家解读一下有关Linux程序替换的相关知识点&#xff0c;如果看完之后对你有一定的启发&#xff0c;那么请留下你的三连&#xff0c;祝大家心想事成&#xff01; C 语 言 专 栏&#xff1a;C语言&#xff1a;从入门…...

使用ResizeObserver观察DOM元素的尺寸变化

文章目录 关于ResizeObserver示例代码示例代码结果如下所示echarts自适应容器div大小示例代码结果如下所示echarts自适应容器大小的方式二 关于ResizeObserver 关于这个Web API&#xff0c;可以看mdn的官网&#xff0c;ResizeObserver - Web API | MDN (mozilla.org)&#xff…...

前端使用Vue和Element实现可拖动弹框效果,且不影响底层元素操作(可拖拽的视频实时播放弹框,底层元素可以正常操作)

简述&#xff1a;在前端开发中&#xff0c;弹框和实时视频播放是常见的需求。这里来简单记录一下&#xff0c;如何使用Vue.js和Element UI实现一个可拖动的弹框&#xff0c;并在其中播放实时视频。同时&#xff0c;确保在拖拽弹框时&#xff0c;底层元素仍然可以操作。 一、项目…...

文华财经多空K变色支撑压力画线趋势波段指标公式

文华财经多空K变色支撑压力画线趋势波段指标公式&#xff1a; VERTLINE(TIME0900,RGB(128,128,255)),DOT; VERTLINE(TIME2100,COLORBLACK),DOT; HH:HHV(HIGH,26); LL:LLV(LOW,26); HH1:BARSLAST((HH > REF(HH,1))); LL1:BARSLAST((LL < REF(LL,1))); PARTLINE((HH…...

tomcat9漏洞CVE-2024-23672

序号 漏洞名称 影响主机个数 1 Apache Tomcat 安全漏洞(CVE-2024-23672) 1/1 2 Apache Tomcat 输入验证错误漏洞(CVE-2024-24549) 1/1 漏洞名称&#xff1a;CVE-2024-23672 影响版本&#xff1a;tomcat9.0.0-M1 to 9.0.85&#xff1b;tomcat8.5.0 to 8.5.98 处理…...

ChatGLM-6B入门

ChatGLM-6B ChatGLM-6B 一、介绍 ChatGLM-6B 是一个开源的、支持中英双语的对话语言模型&#xff0c;基于 General Language Model (GLM) 架构&#xff0c;具有 62 亿参数。结合模型量化技术&#xff0c;用户可以在消费级的显卡上进行本地部署&#xff08;INT4 量化级别下最…...

项目实战--Spring Boot + GraphQL实现实时数据推送

背景 用户体验不断提升而3对实时数据的需求日益增长&#xff0c;传统的数据获取方式无法满足实时数据的即时性和个性化需求。 GraphQL作为新兴的API查询语言&#xff0c;提供更加灵活、高效的数据获取方案。结合Spring Boot作为后端框架&#xff0c;利用GraphQL实现实时数据推…...

ASPICE是汽车软件开发中的质量保证流程

复杂的汽车系统对软件的质量和可靠性提出了极高的要求。为了确保汽车软件的高质量和可靠性&#xff0c;ASPICE&#xff08;Automotive SPICE&#xff0c;汽车软件过程改进和能力确定&#xff09;流程应运而生。本文将对ASPICE流程进行详细介绍。 一、ASPICE概述 ASPICE是汽车行…...

Linux调试器-gdb使用以及Linux项目自动化构建工具-make/Makefile

目录 1.gdb背景2.开始使用gdb3.make/makefile 背景4.实例代码5.依赖关系6.依赖方法7.原理8.项目清理 1.gdb背景 程序的发布方式有两种&#xff0c;debug模式和release模式 Linux gcc/g出来的二进制程序&#xff0c;默认是release模式 要使用gdb调试&#xff0c;必须在源代码生…...

Html5前端基本知识整理与回顾下篇

今天我们继续结合发布的Html5基础知识点文档进行复习&#xff0c;希望对大家有所帮助。 目录 列表 无需列表 有序列表 自定义列表 样例 表格 基本属性 ​编辑 相关属性 Border Width Height ​编辑 表格标题 ​编辑 表格单元头 合并单元格 垂直单元格合并 水…...

vmware 虚拟机扩容 centos 硬盘扩容 kylinos v10扩容

1. 虚拟机先扩容 1.1 关机&#xff0c;并点击系统&#xff0c;让他是点选状态&#xff0c;但是没开机 1.2 右击&#xff0c;点击最下方设置&#xff0c;点击硬盘 1.3 点击扩展磁盘 1.4 选择你需要扩容的大小&#xff0c;数字为总大小 完成提示&#xff1a; 磁盘已成功扩展。您…...

什么样的开放式耳机好用?,五大超强卷王单品推荐!

对于热衷尝试不同耳机类型的小伙伴们而言&#xff0c;经过对佩戴舒适度、音质清晰度及电池续航能力的全面考量&#xff0c;开放式蓝牙耳机因其卓越的平衡性脱颖而出&#xff0c;成为多数人的心头好。其轻巧设计不仅保证了长时间佩戴的舒适感&#xff0c;还兼顾了音质与续航的双…...

java使用poi-tl模版引擎导出word之饼状图生成及循环批量生成饼状图

文章目录 一、单个饼状图生成1.word模版制作2.编写接口完整代码3.导出结果 二、批量生成饼图1.word模版制作2.编写接口完整代码3.导出结果 一、单个饼状图生成 1.word模版制作 在word中创建一个饼状图&#xff0c;点击图表&#xff0c;点击“文本选项”&#xff0c;在可选文字…...

指定版本ceph-common安装

如&#xff0c;安装15.2.13的ceph-common PACKAGE_NAMEceph-common CEPH_VERSION15.2.13 wget -q -O- https://download.ceph.com/keys/release.asc | sudo apt-key add - echo deb http://download.ceph.com/debian-${CEPH_VERSION}/ $(lsb_release -sc) main | sudo tee …...

C++语言特性——关键字(static、volatile、extern、const、mutable、inline)

注意&#xff1a; 本内容为摘抄网上的学习资料&#xff0c;作为个人笔记使用&#xff0c;如有侵权, 立刻删除。 C语言特性 1.关键字 &#xff08;1&#xff09;static static全局变量和普通全局变量 面试高频指数&#xff1a;★★★☆☆ 相同点&#xff1a; 存储方式&…...

在Ubuntu 16.04上安装和配置VNC的方法

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 ###介绍 VNC&#xff0c;即“Virtual Network Computing”&#xff0c;是一种连接系统&#xff0c;允许您使用键盘和鼠标与远程服务器上…...

@RequestBody注解的使用及源码解析

前言 RequestBody 注解是我们进行JavaEE开发&#xff0c;最常见的几个注解之一&#xff0c;这篇博文我们以案例和源码相结合&#xff0c;帮助大家更好的了解 RequestBody 注解 使用案例 1.自定义实体类 Data NoArgsConstructor AllArgsConstructor public class User {priv…...

linux 服务器数据备份 和 mysql 数据迁移

查看域名ip 查看程序所处文件位置 list open files 1、 lsof -i :port 查看端口获取进程 pid 2、lsof -i pid 1、scp 下载服务器文件到本地 security copy protocol 2、导出服务器 mysql 数据库&#xff08;表&#xff09;到本地 mysqldump是MySQL自带的一个实用程序&…...

安防视频监控/云存储/视频汇聚EasyCVR平台播放设备录像不稳定,是什么原因?

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台可拓展性强、视频能力灵活、部署轻快&#xff0c;EasyCVR基于云边端一体化架构&#xff0c;具有强大的数据接入、处理及分发能力&#xff0c;可提供7*24小时实时高清视频监控、云端录像、云存储、录像检索与回看、智能告警…...

S32V234平台开发(一)快速使用

快速使用 准备供电复位选择串口通信启动选择显示登陆系统 准备供电 s32v234可以使用两种电源供电 一种是左边电源端子&#xff0c;一种是右边电源适配器(12V 3A) 注意:不要同时使用两种电源同时供电 复位选择 Pressing POR RESET pulls active low EXT_POR signal on S32V2…...

rknn优化教程(二)

文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK&#xff0c;开始写第二篇的内容了。这篇博客主要能写一下&#xff1a; 如何给一些三方库按照xmake方式进行封装&#xff0c;供调用如何按…...

visual studio 2022更改主题为深色

visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中&#xff0c;选择 环境 -> 常规 &#xff0c;将其中的颜色主题改成深色 点击确定&#xff0c;更改完成...

华为OD机试-食堂供餐-二分法

import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...

Robots.txt 文件

什么是robots.txt&#xff1f; robots.txt 是一个位于网站根目录下的文本文件&#xff08;如&#xff1a;https://example.com/robots.txt&#xff09;&#xff0c;它用于指导网络爬虫&#xff08;如搜索引擎的蜘蛛程序&#xff09;如何抓取该网站的内容。这个文件遵循 Robots…...

Rust 异步编程

Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...

拉力测试cuda pytorch 把 4070显卡拉满

import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试&#xff0c;通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小&#xff0c;增大可提高计算复杂度duration: 测试持续时间&#xff08;秒&…...

.Net Framework 4/C# 关键字(非常用,持续更新...)

一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...

免费PDF转图片工具

免费PDF转图片工具 一款简单易用的PDF转图片工具&#xff0c;可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件&#xff0c;也不需要在线上传文件&#xff0c;保护您的隐私。 工具截图 主要特点 &#x1f680; 快速转换&#xff1a;本地转换&#xff0c;无需等待上…...

前端开发者常用网站

Can I use网站&#xff1a;一个查询网页技术兼容性的网站 一个查询网页技术兼容性的网站Can I use&#xff1a;Can I use... Support tables for HTML5, CSS3, etc (查询浏览器对HTML5的支持情况) 权威网站&#xff1a;MDN JavaScript权威网站&#xff1a;JavaScript | MDN...

AD学习(3)

1 PCB封装元素组成及简单的PCB封装创建 封装的组成部分&#xff1a; &#xff08;1&#xff09;PCB焊盘&#xff1a;表层的铜 &#xff0c;top层的铜 &#xff08;2&#xff09;管脚序号&#xff1a;用来关联原理图中的管脚的序号&#xff0c;原理图的序号需要和PCB封装一一…...