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…...
Spring Boot 实现流式响应(兼容 2.7.x)
在实际开发中,我们可能会遇到一些流式数据处理的场景,比如接收来自上游接口的 Server-Sent Events(SSE) 或 流式 JSON 内容,并将其原样中转给前端页面或客户端。这种情况下,传统的 RestTemplate 缓存机制会…...
【Java学习笔记】Arrays类
Arrays 类 1. 导入包:import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序(自然排序和定制排序)Arrays.binarySearch()通过二分搜索法进行查找(前提:数组是…...
基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...
今日科技热点速览
🔥 今日科技热点速览 🎮 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售,主打更强图形性能与沉浸式体验,支持多模态交互,受到全球玩家热捧 。 🤖 人工智能持续突破 DeepSeek-R1&…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...
蓝桥杯 冶炼金属
原题目链接 🔧 冶炼金属转换率推测题解 📜 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V,是一个正整数,表示每 V V V 个普通金属 O O O 可以冶炼出 …...
安全突围:重塑内生安全体系:齐向东在2025年BCS大会的演讲
文章目录 前言第一部分:体系力量是突围之钥第一重困境是体系思想落地不畅。第二重困境是大小体系融合瓶颈。第三重困境是“小体系”运营梗阻。 第二部分:体系矛盾是突围之障一是数据孤岛的障碍。二是投入不足的障碍。三是新旧兼容难的障碍。 第三部分&am…...
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...
RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)
RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发,后来由Pivotal Software Inc.(现为VMware子公司)接管。RabbitMQ 是一个开源的消息代理和队列服务器,用 Erlang 语言编写。广泛应用于各种分布…...
