Linux->进程程序替换
目录
前言:
1 程序替换原理
2 单进程替换
3 替换函数
3.1 函数使用
4 程序去替换自己的另一个程序操作方式
5 实现自己的shell
前言:
通过我们之前对于子进程的应用,我相信大家一定是能够想到创建子进程的目的之一就是为了代劳父进程执行父进程的部分代码,也就是说本质上来说父子进程都是执行的同一个代码段的数据,在子进程修改数据的时候进行写时拷贝修改数据段的部分数据。
但是还有一个目的大家知道吗?不知道没关系,因为这就是本篇文章的主题,将子进程在运行时指向一个全新的程序代码,也就是我们的进程程序替换。
1 程序替换原理
首先程序替换是将当前进程的全部数据被替换成为了另外的一个程序的代码再运行,那么这样做产生了一个什么样的效果?那就是我们没有必要对一个已经出现的程序再次的写一遍,以及不用在PCB数据结构当中再添加一个新的PCB块。如下图所示:

从上图当中我们可以知道一个进程的出现必须先在PCB结构当中先构建自己的PCB块,有了对应的虚拟地址之后,然后虚拟地址通过页表映射的方式在物理地址当中找到位置,然后将磁盘当中的程序加载进入对应的物理地址。
我们进程替换所做的事情就是不改变PCB的情况之下,后续的操作重新做了一遍,也就是说操作系统在这一块进行了全面的写实拷贝,连代码段的数据都被修改了。保证了代码的独立性。
2 单进程替换
只看上面的解释,我相信小伙伴们对于这一块还是不太理解,那么上代码:
7 int main()8 {9 printf("begin---\n");10 execl("/bin/ls","ls","-a","-l",NULL);11 printf("end----\n");12 return 0;13 }
我们可以看到,我们主程序当中写下了两个printf,分别在execl函数的前后,execl里面有我们熟悉的指令程序,也就是ls,先不管execl是什么,先观察效果。

发现了什么,我们在execl后面的printf没有被输出,也就证明了我们的程序替换是完全替换,会将我们的代码完全变为另外一个程序的代码。
3 替换函数
#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[]);
由我们库封装的exec函数常用的有上面的几种,这些函数都有下面的特性,当该函数成功执行,那么进程替换成功,代码不再返回,如果函数调用失败,例如不正确的地址,不正确的文件等等,函数会返回一个-1,并且exec函数之后在函数调用失败时才有返回值,成功没有返回值。
只有失败时有返回值其实很好理解,因为函数调用成功那就表示程序替换成功,原来的代码都被替换了,我返回之后给谁?没了,之后的代码就是新代码了。
3.1 函数使用
使用这些函数其实简单,先将函数名的exec提取出来看后面的几个字母。
l:表示用列表方式传递。
![]()
其中/bin/ls表示需要执行的文件是谁,ls表示执行方式,而-a和-l表示这个执行的参数列表。
v:表示使用数组的方式传递。

可以看到我们用过指针数组的方式将我们的执行和参数列表存到了一起,然后将这个指针数组作为参数传递给我们的execv函数就行。
p:表示自己只需要传递需要执行的文件是谁,操作系统会从默认环境变量当中去查找。
![]()
e:表示可以传递自己的环境变量。

注意:当我们传递自己的环境变量时会替换默认环境变量,所以如果想要添加一个环境变量,而不是替换那就需要下方的操作。

通过系统提供的存环境变量的environ变量,在用putenv函数添加自己的环境变量,以达到添加环境变量的操作。
上面的几个字母通过不同的组合可以达到不同的操作方式。
4 程序去替换自己的另一个程序操作方式
7 int main()8 {9 pid_t id = fork(); 10 if(id == 0)11 {12 extern char** environ;13 printf("begin+++++++++++++++++++++\n");14 printf("begin+++++++++++++++++++++\n");15 printf("begin+++++++++++++++++++++\n");16 printf("begin+++++++++++++++++++++\n");17 printf("我是子进程,PID是:%d\n",getpid());18 char arg[] = "MYENV=You Can See Me";19 putenv(arg);//替换程序在exec目录下,执行文件名字是otherproc20 execle("./exec/otherproc","otherproc",NULL,environ); 21 printf("end++++++++++++++++++++++\n"); 22 printf("end++++++++++++++++++++++\n"); 23 printf("end++++++++++++++++++++++\n"); 24 printf("end++++++++++++++++++++++\n"); 25 exit(1); 26 } 27 28 sleep(2); 29 int status = 0; 30 waitpid(id,&status,0); 31 printf("我是父进程,PID是:%d,子进程退出状态是:%d\n",getpid(),WEXITSTATUS(status)); 32 33 return 0;34 }


我们需要从原程序中替换到这个otherproc这个执行文件。
使用函数:execle("./exec/otherproc","otherproc",NULL,environ);

5 实现自己的shell
通过上面的学习相信大家对进程替换已经有了足够的了解了,那么大家有没有想过我们的Linux通过shell是怎么运行的呢?我们都知道我们创建的所有进程都是bash的子进程,那么我们是不是也可以写一个自己的bash,然后可以使用这些指令呢?答案是可以,请看下方代码。
1 #include <stdio.h>2 #include <unistd.h>3 #include <sys/types.h>4 #include <wait.h>5 #include <string.h>6 #include <assert.h>7 #include <stdlib.h>8 9 #define MAX_STR 102410 #define ARG 6411 #define SPE " "12 13 int splite(char commandstr[], char* argv[])14 {15 assert(commandstr);16 assert(argv);17 argv[0] = strtok(commandstr,SPE);18 if(argv[0] == NULL) return -1;19 20 int i = 1;21 while((argv[i++] = strtok(NULL,SPE)));22 23 return 0;24 }25 26 void show_command(char* argv[]) 27 {28 int i = 0;29 while(argv[i] != NULL)30 {31 printf("%d,%s ",i,argv[i]);32 i++;33 }34 printf("\n");35 }36 37 int main()38 {39 while(1) 40 {41 //接收从界面接收的字符数组42 char commandstr[MAX_STR] = {0};43 44 //将我们的命令通过空格的方式切割成为一个一个的字符串45 //第一个是我们的指令,后续的是参数列表46 char* argv[ARG] = {NULL};47 48 //我们的shell界面提示49 printf("[zhangshan@mymachine test ]# ");50 51 //因为我们希望直接看到上面的打印,而不是等缓冲区运行完毕52 fflush(stdout);53 54 //将输入的字符获取,并赋值给commandstr数组当中55 char* s = fgets(commandstr,sizeof(commandstr),stdin);56 assert(s);57 58 //因为s这个变量我们并没有实际的使用,所以用一个不会做任何改变的操作使用59 (void)s;60 61 //我们输入指令时回安回车表示输入完毕,而我们不希望操作系统录入这个\n所以需要更改62 commandstr[strlen(commandstr)-1] = '\0';63 64 //将字符切割成为小字符串,然后赋值给argv这个指针数组当中65 int n = splite(commandstr,argv);66 show_command(argv); 67 //如果没有获取有效的命令,直接跳过本次循环,也就是指令失效68 if(n != 0) continue;69 70 //创建子进程,用于后续的程序替换71 //因为程序替换需要完全替换后续的代码,也就是说如果用父进程去替换72 //就不会有下一次的循环了,这不是我们期望的结果,所以建子进程73 pid_t id = fork();74 75 if(id == 0)76 {77 //通过有默认路径,查询目录加上指针数组的方式exec方式进行进程程序替换78 execvp(argv[0],argv);79 //替换之后是不应该被执行的,如果执行了则表示程序替换错误80 exit(1);81 }82 int status = 0;83 84 //只是回收子进程的退出信息,没有其余的操作,所以不用WNOHANG,也没有后续的代码85 waitpid(id,&status,0);86 }87 }
该代码有很多细节,但是博主也不打算过多的解释了,因为博主已经在主程序的每一句话都写了注解,如果大家有兴趣可以自行阅读,难度不大。
运行结果:

以上就是博主对于进程替换的全部理解了,希望能够帮到大家。
相关文章:
Linux->进程程序替换
目录 前言: 1 程序替换原理 2 单进程替换 3 替换函数 3.1 函数使用 4 程序去替换自己的另一个程序操作方式 5 实现自己的shell 前言: 通过我们之前对于子进程的应用,我相信大家一定是能够想到创建子进程的目的之一就是为了代劳父进程执…...
最强分布式锁工具:Redisson
1 Redisson概述1.1 什么是Redisson?Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务。其中包括(BitSet, Set, Multimap, Sorted…...
Java9-17新特性
Java9-17新特性 一、接口的私有方法 Java8版本接口增加了两类成员: 公共的默认方法公共的静态方法 Java9版本接口又新增了一类成员: 私有的方法 为什么JDK1.9要允许接口定义私有方法呢?因为我们说接口是规范,规范时需要公开…...
电脑开机找不到启动设备怎么办?
电脑正常开机,却提示“找不到启动设备”,这时我们该怎么办呢?本文就为大家介绍几种针对该问题的解决方法,一起来看看吧!“找不到启动设备”是什么意思?可引导设备(又称启动设备)是一…...
使用langchain打造自己的大型语言模型(LLMs)
我们知道Openai的聊天机器人可以回答用户提出的绝大多数问题,它几乎无所不知,无所不能,但是由于有机器人所学习到的是截止到2021年9月以前的知识,所以当用户询问机器人关于2021年9月以后发送的事情时,它无法给出正确的答案&#x…...
assert()宏函数
assert()宏函数 assert是宏,而不是函数。在C的assert.h文件中 #include <assert.h> void assert( int expression );assert的作用是先计算表达式expression, 如果其值为假(即为0),那么它会打印出来assert的内容…...
Docker圣经:大白话说Docker底层原理,6W字实现Docker自由
说在前面: 现在拿到offer超级难,甚至连面试电话,一个都搞不到。 尼恩的技术社群(50)中,很多小伙伴凭借 “左手云原生右手大数据”的绝活,拿到了offer,并且是非常优质的offer&#…...
Redis+Caffeine多级(二级)缓存,让访问速度纵享丝滑
目录多级缓存的引入多级缓存的优势CaffeineRedis实现多级缓存V1.0版本V2.0版本V3.0版本多级缓存的引入 在高性能的服务架构设计中,缓存是一个不可或缺的环节。在实际的项目中,我们通常会将一些热点数据存储到Redis或MemCache这类缓存中间件中࿰…...
C#和.net框架之第一弹
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录C# 简介一、微软平台的编程二、使用VS创建第一个c#程序1、第一步2、第二步3、第三步4、第四步5、第五步C# 简介 C# 是一个现代的、通用的、面向对象的编程语言&…...
C++---背包模型---潜水员(每日一道算法2023.3.12)
注意事项: 本题是"动态规划—01背包"和"背包模型—二维费用的背包问题"的扩展题,优化思路不多赘述,dp思路会稍有不同,下面详细讲解。 题目: 潜水员为了潜水要使用特殊的装备。 他有一个带2种气体…...
C++类的成员变量和成员函数详解
类可以看做是一种数据类型,它类似于普通的数据类型,但是又有别于普通的数据类型。类这种数据类型是一个包含成员变量和成员函数的集合。 类的成员变量和普通变量一样,也有数据类型和名称,占用固定长度的内存。但是,在定义类的时候不能对成员变量赋值,因为类只是一种数据类…...
(枚举)(模拟)(位运算)116. 飞行员兄弟
目录 题目链接 一些话 切入点 流程 套路 ac代码 题目链接 116. 飞行员兄弟 - AcWing题库 我草,又~在~水~字~数~啦!我草,又~在~水~字~数~啦…...
详解Array.prototype.shift.call(arguments)
经常看到如下代码: function foo() {let k Array.prototype.shift.call(arguments);console.log(k) } foo(11,22) //11 Array.prototype.shift.call(arguments)的作用是: 取 arguments 中的第一个参数 一、为啥要这么写,不直接使用argume…...
Tina_Linux_Wi-Fi_开发指南
Tina Linux Wi-Fi 开发指南 1 前言 1.1 文档简介 介绍Allwinner 平台上Wi-Fi 驱动移植,介绍Tina Wi-Fi 管理框架,包括Station,Ap 以及Wi-Fi 常见问题。 1.2 目标读者 适用Tina 平台的广大客户和对Tina Wi-Fi 感兴趣的同事。 1.3 适用范…...
Spring AOP(AOP概念、组成、Spring AOP实现及实现原理)
文章目录1. Spring AOP 是什么2. 为什么要用 AOP3. 怎么学 Spring AOP4. AOP 组成5. Spring AOP 实现5.1 添加 Spring AOP 框架支持5.2 定义切面和切点5.3 实现通知方法5.4 使⽤ AOP 统计 UserController 每个⽅法的执⾏时间 StopWatch5.4 切点表达式说明 AspectJ6. Spring AOP…...
8.条件渲染指令
目录 1 v-if v-show 2 v-if v-else-if v-else 1 v-if v-show v-if与v-show都可以控制DOM的显示与隐藏 由于flag是布尔值,所以这里可以直接写 v-if"flag" 当flag为true的时候,v-if与v-show控制的div都会被显示出来 当flag为false的时候&a…...
2023年全网最全最细最流行的自动化测试工具有哪些?你都知道吗!
下面就是我个人整理的一些比较常用的自动化测试工具,并且还有视频版本的详细介绍,同时在线学习人数超过1000人! B站讲的最详细的Python接口自动化测试实战教程全集(实战最新版)一:前言 随着测试工程师技能和…...
网络安全——数据链路层安全协议
作者简介:一名云计算网络运维人员、每天分享网络与运维的技术与干货。 座右铭:低头赶路,敬事如仪 个人主页:网络豆的主页 目录 前言 一.数据链路层安全协议简介 1.数据链路安全性 二.局域网数据链路层协议 1.…...
编译原理基础概念
一、什么是编译程序编译程序是一种程序,能够将某一种高级语言编写的源程序改造成另一种低级语言编写的目标程序,他们在逻辑上等价、完成相同的工作二、编译阶段1、当目标程序是机器语言时,编译阶段:(1)编译…...
蔬菜视觉分拣机器人的设计与实现(RoboWork参赛方案)
蔬菜视觉分拣机器人的设计与实现 文章目录蔬菜视觉分拣机器人的设计与实现1. 技术栈背景2. 整体设计3. 机械结构3.1 整体结构3.2 底座结构3.3 小臂结构3.4 大臂结构3.5 负载组件结构3.6 末端执行器结构4. 硬件部分4.1 视觉系统4.1.1 光源4.1.2 海康工业相机4.2 传送带系统4.2.1…...
STM32 LWIP服务器内存泄漏踩坑实录:我是如何实现多客户端连接并稳定运行72小时的
STM32 LWIP服务器内存泄漏排查与多客户端连接优化实战 在嵌入式网络应用中,STM32结合LWIP协议栈构建TCP服务器是常见方案。但当系统需要支持多客户端并发连接并长期运行时,内存管理问题往往成为稳定性的最大威胁。本文将分享一个真实案例:如何…...
最新OpenClaw 2.7.1 Windows 环境快速部署教程
Windows 一键部署 OpenClaw v2.7.1 教程|5 分钟搭建本地 AI 智能体 在开源 AI 工具持续更新的当下,OpenClaw(小龙虾)凭借本地运行、零代码操控、自动化执行等特点,成为广受用户欢迎的本地 AI 智能体,GitHu…...
终极指南:如何5分钟搞定B站字幕提取与格式转换
终极指南:如何5分钟搞定B站字幕提取与格式转换 【免费下载链接】BiliBiliCCSubtitle 一个用于下载B站(哔哩哔哩)CC字幕及转换的工具; 项目地址: https://gitcode.com/gh_mirrors/bi/BiliBiliCCSubtitle 你是否曾为保存B站视频中的精彩内容而烦恼?…...
Icarus Verilog终极指南:3分钟掌握开源Verilog仿真工具
Icarus Verilog终极指南:3分钟掌握开源Verilog仿真工具 【免费下载链接】iverilog Icarus Verilog 项目地址: https://gitcode.com/gh_mirrors/iv/iverilog 你是否正在寻找一个完全免费、跨平台的Verilog仿真解决方案?Icarus Verilog(…...
100+ RPG Maker MV/MZ插件:零代码打造专业级游戏体验的完整指南
100 RPG Maker MV/MZ插件:零代码打造专业级游戏体验的完整指南 【免费下载链接】RPGMakerMV RPGツクールMV、MZで動作するプラグインです。 项目地址: https://gitcode.com/gh_mirrors/rp/RPGMakerMV 你是否曾梦想用RPG Maker制作出媲美商业游戏的视觉效果和…...
从LightDM到DWM:打造轻量级Linux桌面启动链
1. 为什么选择LightDMDWM组合 如果你正在寻找一个既轻量又高度可定制的Linux桌面环境,LightDM搭配DWM的组合绝对值得考虑。我用了整整三个月时间测试各种显示管理器和窗口管理器的搭配,最终发现这套方案在资源占用和操作效率上达到了完美平衡。 先说说Li…...
面试题:模型评价指标全解析——准确率、精确率、召回率、F1、ROC、AUC、MAE、MSE、RMSE、R² 一文讲透
把“分类指标怎么看、回归指标怎么选、ROC/AUC 怎么判断模型好坏”一次讲清楚很多人在面试里被问到“模型评价指标有哪些”时,第一反应往往是背一串名词:准确率、精确率、召回率、F1、AUC、MAE、MSE、R。看似都答到了,实际上却很容易被继续追…...
1、Chrome Elements面板:从入门到精通的网页调试实战指南
1. Chrome Elements面板:你的网页调试瑞士军刀 第一次打开Chrome开发者工具时,那个标着"Elements"的标签页看起来就像是一堆杂乱无章的HTML代码。但当我真正开始理解它的功能后,它迅速成为了我每天使用最频繁的开发工具。Elements面…...
AI架构绘图副驾驶:用自然语言生成专业Excalidraw架构图
1. 项目概述与核心价值 如果你和我一样,每天都要面对陌生的、动辄几十个微服务的复杂代码库,或者需要向团队解释一个新系统的设计,那你一定理解那种“认知过载”的痛苦。在脑海里构建整个系统的架构图,试图理清服务间的调用关系和…...
SAP S/4HANA 利润中心(PCA)完整配置步骤
SAP S/4HANA 利润中心(PCA)完整配置步骤按项目上线标准顺序一步步来,从零到可用,含前台 后台、必配 可选,通俗易懂不绕弯路一、前期基础前提(必须先做好)公司代码、控制范围已创建控制范围与公…...
