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…...

CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...
TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案
一、TRS收益互换的本质与业务逻辑 (一)概念解析 TRS(Total Return Swap)收益互换是一种金融衍生工具,指交易双方约定在未来一定期限内,基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...

优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...

Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...
现有的 Redis 分布式锁库(如 Redisson)提供了哪些便利?
现有的 Redis 分布式锁库(如 Redisson)相比于开发者自己基于 Redis 命令(如 SETNX, EXPIRE, DEL)手动实现分布式锁,提供了巨大的便利性和健壮性。主要体现在以下几个方面: 原子性保证 (Atomicity)ÿ…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现企业微信功能
1. 开发环境准备 安装DevEco Studio 3.1: 从华为开发者官网下载最新版DevEco Studio安装HarmonyOS 5.0 SDK 项目配置: // module.json5 {"module": {"requestPermissions": [{"name": "ohos.permis…...

基于PHP的连锁酒店管理系统
有需要请加文章底部Q哦 可远程调试 基于PHP的连锁酒店管理系统 一 介绍 连锁酒店管理系统基于原生PHP开发,数据库mysql,前端bootstrap。系统角色分为用户和管理员。 技术栈 phpmysqlbootstrapphpstudyvscode 二 功能 用户 1 注册/登录/注销 2 个人中…...

【技巧】dify前端源代码修改第一弹-增加tab页
回到目录 【技巧】dify前端源代码修改第一弹-增加tab页 尝试修改dify的前端源代码,在知识库增加一个tab页"HELLO WORLD",完成后的效果如下 [gif01] 1. 前端代码进入调试模式 参考 【部署】win10的wsl环境下启动dify的web前端服务 启动调试…...

[10-1]I2C通信协议 江协科技学习笔记(17个知识点)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17...