【Linux】进程程序替换
文章目录
- 替换原理
- 站在进程的角度
- 站在程序的角度
- 初体验及理解原理
- 替换函数
- 函数解释
- 命名理解
- exec系列函数与main函数之间的关系
- 在一个程序中调用我们自己写的程序
替换原理
创建子进程的目的是什么?
->想让子进程执行父进程代码的一部分执行父进程对应的磁盘代码中的一部分
->想让子进程执行一个全新的程序
让子进程想办法加载磁盘上指定的程序,执行新程序的代码和数据
->
进程的程序替换
站在进程的角度
没有创建新的进程(PCB尤其是pid是没有变化的),只是程序的替换
站在程序的角度
- 这个程序被加载到内存中了->称
exec函数
为"加载器" - 既然我自己的代码能加载到新的程序,操作系统也是
- 先创造进程数据结构(PCB),在再需要的时候加载代码和数据
初体验及理解原理
int execl(const char *path, const char *arg, ...);
将指定的程序加载到内存中,让制定进程进行执行需要解决两个问题:1.找到程序 2.如何执行(cmd 选项一 选项二)
后面的三个点:可变参数列表:可以给函数不同数量的参数,例如:printf\scanf
示范程序:
#include<stdio.h>
#include<unistd.h>int main()
{//.c->exe->load->process->运行->执行我们现在所写的程序printf("process is running...\n");//load->exeexecl("/usr/bin/ls"/*要执行哪一个程序*/, "ls", "-a","-l",NULL/*你想怎么执行*/);//所有的exel程序都必须以NULL结尾,证明你把参数传完了//但是此时就不能打印出下面的句子
//因为他也是代码,是在execl的,execl执行完毕的时候,代码已经被完全覆盖,开始执行新的程序的代码了,所以printf就无法执行了!
//但是如果调用失败了,就是没有替换成功,就会打印出来下面的句子printf("process running done...\n");
}
由上述的例子我们可以看出这个函数的特点:
只有失败才会返回-1(只需要做错误处理,即:只要返回值,就一定是调用错误了)
perror("execl");
可以打印错误原因.
调用成功时则不返回,因为此时已经替换了,后续的代码已经没有意义了,没有必要返回值了.(成功了就和接下来的代码无关了)
用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变
由虚拟地址空间+页表来保证进程的独立性,一旦有执行流想替换代码或者数据,就发生写时拷贝.->说明写时拷贝在代码区也可以发生的!
但是我们一般不直接在父进程进行程序替换,而是创建新的子进程进行程序替换.
于是我们优化上面的代码:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<assert.h>
##include<sys/types.h>
#include<sys/wait.h>
int main()
{printf("process is running...\n");pid_t id = fork();assert(id != -1);if (id == 0){//这里的替换会影响父进程吗?//不会。进程具有独立性,在执行exec时会发生写时拷贝//类比:命令行怎么写,这里就怎么传参sleep(1);execl("/usr/bin/ls", "ls", "-a", "-l", "--color=auto", NULL);exit(-1);//must failed}int status = 0;pid_t ret = waitpid(id,&status,0);if (ret > 0){printf("wait success: \nexit code: %d,sig: %d\n",(status>>8)&0xFF,status&0x7F);}}
替换函数
其实有六种以exec开头的函数,统称exec函数:
#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[]);
int execve(const char *path, char *const argv[], char *const envp[]);
其中,
execve
是系统调用,其余的都是封装,是为了让我们有更多的选择性
函数解释
这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。
如果调用出错则返回-1
所以exec函数只有出错的返回值而没有成功的返回值
不用对该函数进行返回值判断,只要继续向后运行一定是失败的
命名理解
这些函数原型看起来很容易混,但只要掌握了规律就很好记。
l(list) : 表示参数采用列表
v(vector) : 参数用数组
可以将所有的执行参数放入数组中,统一传递,而不用进行使用可变参数方案.
char* const argv_[] = {"-ls","-a","-l","--color=auto",NULL
};
execv("usr/bin/ls",argv_);
p(path) : 有p自动搜索环境变量PATH
带p字符的函数,不用告诉我程序的路径,你只要告诉我程序的名字,我会自动在环境变量PATH中进行可执行程序的查找.
execlp("ls", "ls", "-a", "-l", "--color=auto", NULL);
这里前面的ls是告诉系统我要执行谁,第二个是告诉系统我想要怎么执行
e(env) : 表示自己维护环境变量
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<assert.h>
##include<sys/types.h>
#include<sys/wait.h>
int main()
{printf("process is running...\n");pid_t id = fork();assert(id != -1);if (id == 0){sleep(1);char* const envp_[] = {(char*)"MYENV=1122233334444",NULL;}extern char** environ;//声明系统默认的环境变量列表execle("./mybin","mybin",NULL,environ);//这里是只传了系统默认的环境变量putenv((char*)"MYENV=444333222111");//将指定环境变量导入系统中environ指向对应的环境变量表中execle("./mybin","mybin",NULL,envp_);//这里是只传了你自定义的环境变量execle("./mybin","mybin",NULL,environ);//实际上,默认环境变量你不传,子进程也能获取. 这里是传了默认的环境变量加上你自己添加的环境变量exit(1);}int status = 0;pid_t ret = waitpid(id,&status,0);if (ret > 0){printf("wait success: \nexit code: %d,sig: %d\n",(status>>8)&0xFF,status&0x7F);}}
exec系列函数与main函数之间的关系
exec*系列函数的意义是:
将我们的程序加载到内存中
int main(int argc,char* argv[],char* env[])
所以应该是先加载到内存中,之后才是调用main函数,main函数中的argc
argc[]
分别是exec函数中的前面的两个参数,env[]
是从exec函数的环境变量参数中加载出来的
exec调用举例如下:
#include <unistd.h>
int main()
{char* const argv[] = { "ps", "-ef", NULL };char* const envp[] = { "PATH=/bin:/usr/bin", "TERM=console", NULL };execl("/bin/ps", "ps", "-ef", NULL);// 带p的,可以使用环境变量PATH,无需写全路径execlp("ps", "ps", "-ef", NULL);// 带e的,需要自己组装环境变量execle("ps", "ps", "-ef", NULL, envp);execv("/bin/ps", argv);// 带p的,可以使用环境变量PATH,无需写全路径execvp("ps", argv);// 带e的,需要自己组装环境变量//没有带e的话会默认传入系统默认的环境变量execve("/bin/ps", argv, envp);exit(0);
}
在一个程序中调用我们自己写的程序
首先,我们需要在makefile文件中同时创建两个文件并且同时删除两个文件,那该怎么书写呢?
.PHONY:all
all: mybin myexecmybin:mybin.cgcc -o $@ $^ -std=c99
myexec:myexec.cgcc -o $@ $^ -std=c99 .PHONY:clean
clean:rm -f myexec mybin
myexec.c
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<assert.h>
##include<sys/types.h>
#include<sys/wait.h>
int main()
{printf("process is running...\n");pid_t id = fork();assert(id != -1);if (id == 0){execl("./mybin", "mybin", NULL);exit(-1);}int status = 0;pid_t ret = waitpid(id,&status,0);if (ret > 0){printf("wait success: \nexit code: %d,sig: %d\n",(status>>8)&0xFF,status&0x7F);}}
可以使用程序替换调用任何后端语言所对应的可执行!!!
事实上,只有execve是真正的系统调用,其它五个函数最终都调用 execve,所以execve在man手册 第2节,其它函数在man手册第3节。这些函数之间的关系如下图所示。
下图exec函数族 一个完整的例子:
相关文章:

【Linux】进程程序替换
文章目录 替换原理站在进程的角度站在程序的角度初体验及理解原理 替换函数函数解释命名理解exec系列函数与main函数之间的关系在一个程序中调用我们自己写的程序 替换原理 创建子进程的目的是什么? ->想让子进程执行父进程代码的一部分 执行父进程对应的磁盘代码…...

项目构建工具maven的基本配置+idea 中配置 maven
👑 博主简介:知名开发工程师 👣 出没地点:北京 💊 2023年目标:成为一个大佬 ——————————————————————————————————————————— 版权声明:本文为原创文…...

【解密ChatGPT】:从过去到未来,揭示其发展与变革
🎊专栏【ChatGPT】 🌺每日一句:天行健,君子以自强不息,地势坤,君子以厚德载物 ⭐欢迎并且感谢大家指出我的问题 文章目录 一、ChatGPT的发展历程 二、ChatGPT的技术原理 三、ChatGPT的应用场景 四、ChatGPT的未来趋势 五、总结 引言:随着…...
系统架构设计】计算机公共基础知识: 5 数学与经济管理
一 运筹方法 1 线性规划 线性规划问题的数学模型通常由线性目标函数、线性约束条件、变量非负条件组成,特点如下: (1)线性规划的可行解域是由一组线性约束条件形成的。 (2)如果存在两个最优解,则连接这两点的线段内所有的点都是最优解,而线段两端延长线上可能会超出…...
Visual Studio 2019光标变成灰色方块问题
文章目录 Visual Studio 2019光标变成灰色方块问题问题描述解决方案 Visual Studio 2019光标变成灰色方块问题 问题描述 单击和双击都无法选中单词,总是选择整行或者是当前光标处的前几个字符一起选中,没有规则,貌似选择单词复制࿰…...

C++ http协议POST body raw 字段向服务器发送请求
环境:ubuntu系统c使用http协议不是很方便,通过curl库我们可以很方便使用http协议,由于我的请求方式比较特殊,在网上没有找到相关的资料,之前使用python实现过一版,但是当设备数量超过100台时,程…...
通过migrate命令实现两个redis实例之间的数据迁移
本文适用于将源服务器的redis实例的key的数据迁移到其他服务器的redis实例 一、migrate简介: migrate用于在Redis实例间进行数据迁移,实际上migrate命令是将dump、restore、del三个命令进行组合,从而简化了操作流程。migrate命令具有原子性&…...

Unity 判断两个UI是否相交
今天碰到要判断两个UI是否相交的交互。 尝试了下,发现有两个方法都成功了。 1、使用Collider2D组件 分别创建两个Image组件,并且添加Collider2D组件,其中一个还要添加Rigidbody2D组件,如下图: 然后创建个判断脚本“…...
swoole process 消息通信
swoole文档:Swoole 文档 process子进程和父进程之间通信,依靠监听。子进程和父进程分别做监听。父进程写入信息,子进程监听接收。子进程向父进程写入,调用父进程监听。 子进程向父进程写入信息有两种方式,一种调用wr…...
uniapp跳转方式
UniApp 是一个基于 Vue.js 的跨平台开发框架,可以同时构建 iOS、Android、H5 和小程序等多个平台的应用。在 UniApp 中,我们可以使用以下几种方式进行页面之间的跳转: 使用 uni.navigateTo 方法:该方法可以在当前页面打开新的页面…...

六大排序算法:插入、选择、冒泡、快排、希尔、归并
1、插入排序 解析:第一个元素设定为已经排好序,依次选择后续的元素插入到已经排好序的组内进行排序。 图示: 代码: public static void insertionSort(int[] arr) {int n arr.length;for (int i 1; i < n; i) {int key a…...

短信登录实现(黑马点评为例)
文章目录 前言一、隐藏用户敏感信息二、短信验证登录、注册1.流程2.代码3.使用redis优化解决代码 二、登录拦截(校验)1.流程2.代码 总结 前言 短信登录核心知识 首先黑马点评这个短信登录是一伪验证,即后台调用工具类随机生成六位数字。 1.R…...

【uniapp】签名组件,兼容vue2vue3
网上找了个源码改吧改吧,清除了没用的功能和兼容性,基于uniapp开发的 样子 vue2 使用方法,具体的可以根据业务自行修改 <signature ref"signature" width"100%" height"410rpx"></signature>confi…...

初步利用Ansible实现批量服务器自动化管理
1.Ansible介绍 Ansible是一款开源的自动化运维工具, 在2012年由Michael DeHaan创建, 现在由Red Hat维护。Ansible是基于Python开发的,采用YAML语言编写自动化脚本playbook, 可以在Linux、Unix等系统上运行, 通过SSH协议管理节点, 无需在被管理节点安装agent。Ansible以其简单、…...
网络安全和隐私保护技术
一、定义 网络安全和隐私保护技术是指在互联网和其他网络环境中,通过技术手段保护网络系统、网络数据和用户隐私免于受到恶意攻击、非法访问、窃取或滥用。网络安全和隐私保护技术是保护网络安全和用户隐私的重要手段,是保障互联网和其他网络环境正常运…...
保险行业采购管理痛点及解决方案(数智化采购系统)
随着社会发展,个人和企业有了更多的金融保险需求。对于金融保险公司而言,需要在采购合规的基础上,基于数智化能力,让经营变得更加高效和智能。 1、围绕重点领域,业务加速布局。 保险行业结合自身业务经营重点&#x…...

光学仿真 | 仿真推动以人类视觉感知为本的汽车显示设计
如果产品设计无法使终端用户产生共鸣,就不会存在卓越的工程设计。您可以设计一种结构坚固的方向盘,但如果它被放在错误的位置,就无法实现其用于转向的主要目的。 同样,在围绕人类视觉进行设计时,显示器其实无需具备尽…...

判断两个对象是否不相等operator.ne()
【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 判断两个对象是否不相等 operator.ne() 选择题 下列代码执行输出的结果是? import operator print("【执行】operator.ne(8,8)") print(operator.ne(8,8)) print("【执行】…...

2023年云计算发展趋势:生活的智能未来
目录 引言1 智能家居的崭新时代2 无人驾驶的崭新时代3 虚拟现实的扩展与改进4 人工智能的综合应用5 云计算的可持续性结语 引言 时光荏苒,科技的飞速发展已经成为当今社会的标志之一。在这个数字化时代,云计算已经成为推动技术革新和生活方式改变的关键…...

Spring Boot项目中通过 Jasypt 对属性文件中的账号密码进行加密
下面是在Spring Boot项目中对属性文件中的账号密码进行加密的完整步骤,以MySQL的用户名为root,密码为123321为例: 步骤1:引入Jasypt依赖 在项目的pom.xml文件中,添加Jasypt依赖: <dependency><…...

springboot 百货中心供应链管理系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,百货中心供应链管理系统被用户普遍使用,为方…...
应用升级/灾备测试时使用guarantee 闪回点迅速回退
1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间, 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点,不需要开启数据库闪回。…...

JavaScript 中的 ES|QL:利用 Apache Arrow 工具
作者:来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗?了解下一期 Elasticsearch Engineer 培训的时间吧! Elasticsearch 拥有众多新功能,助你为自己…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...
pam_env.so模块配置解析
在PAM(Pluggable Authentication Modules)配置中, /etc/pam.d/su 文件相关配置含义如下: 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块,负责验证用户身份&am…...

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...

让AI看见世界:MCP协议与服务器的工作原理
让AI看见世界:MCP协议与服务器的工作原理 MCP(Model Context Protocol)是一种创新的通信协议,旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天,MCP正成为连接AI与现实世界的重要桥梁。…...
06 Deep learning神经网络编程基础 激活函数 --吴恩达
深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...

均衡后的SNRSINR
本文主要摘自参考文献中的前两篇,相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程,其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt 根发送天线, n r n_r nr 根接收天线的 MIMO 系…...
iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈
在日常iOS开发过程中,性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期,开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发,但背后往往隐藏着系统资源调度不当…...