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…...
Admin.Net中的消息通信SignalR解释
定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...
在四层代理中还原真实客户端ngx_stream_realip_module
一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡(如 HAProxy、AWS NLB、阿里 SLB)发起上游连接时,将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后,ngx_stream_realip_module 从中提取原始信息…...
高防服务器能够抵御哪些网络攻击呢?
高防服务器作为一种有着高度防御能力的服务器,可以帮助网站应对分布式拒绝服务攻击,有效识别和清理一些恶意的网络流量,为用户提供安全且稳定的网络环境,那么,高防服务器一般都可以抵御哪些网络攻击呢?下面…...

html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码
目录 一、👨🎓网站题目 二、✍️网站描述 三、📚网站介绍 四、🌐网站效果 五、🪓 代码实现 🧱HTML 六、🥇 如何让学习不再盲目 七、🎁更多干货 一、👨…...
Python ROS2【机器人中间件框架】 简介
销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek
文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama(有网络的电脑)2.2.3 安装Ollama(无网络的电脑)2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...

DBLP数据库是什么?
DBLP(Digital Bibliography & Library Project)Computer Science Bibliography是全球著名的计算机科学出版物的开放书目数据库。DBLP所收录的期刊和会议论文质量较高,数据库文献更新速度很快,很好地反映了国际计算机科学学术研…...

Vue3 PC端 UI组件库我更推荐Naive UI
一、Vue3生态现状与UI库选择的重要性 随着Vue3的稳定发布和Composition API的广泛采用,前端开发者面临着UI组件库的重新选择。一个好的UI库不仅能提升开发效率,还能确保项目的长期可维护性。本文将对比三大主流Vue3 UI库(Naive UI、Element …...

Java数组Arrays操作全攻略
Arrays类的概述 Java中的Arrays类位于java.util包中,提供了一系列静态方法用于操作数组(如排序、搜索、填充、比较等)。这些方法适用于基本类型数组和对象数组。 常用成员方法及代码示例 排序(sort) 对数组进行升序…...

Linux-进程间的通信
1、IPC: Inter Process Communication(进程间通信): 由于每个进程在操作系统中有独立的地址空间,它们不能像线程那样直接访问彼此的内存,所以必须通过某种方式进行通信。 常见的 IPC 方式包括&#…...