【Linux】进程程序替换
文章目录
- 替换原理
- 站在进程的角度
- 站在程序的角度
- 初体验及理解原理
- 替换函数
- 函数解释
- 命名理解
- exec系列函数与main函数之间的关系
- 在一个程序中调用我们自己写的程序
替换原理
创建子进程的目的是什么?
->想让子进程执行父进程代码的一部分执行父进程对应的磁盘代码中的一部分
->想让子进程执行一个全新的程序
让子进程想办法加载磁盘上指定的程序,执行新程序的代码和数据
->进程的程序替换
![![[进程控制 2023-01-30 15.38.00.excalidraw|700]]](https://img-blog.csdnimg.cn/f8f465e1eeaf4ceab5ce5c5ae61fdb32.png)
站在进程的角度
没有创建新的进程(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并未改变
![![[Pasted image 20221115142310.png]]](https://img-blog.csdnimg.cn/2055ca4ee46645d4a620d27b2cbfe4d5.png)
由虚拟地址空间+页表来保证进程的独立性,一旦有执行流想替换代码或者数据,就发生写时拷贝.->说明写时拷贝在代码区也可以发生的!
但是我们一般不直接在父进程进行程序替换,而是创建新的子进程进行程序替换.
于是我们优化上面的代码:
#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函数的环境变量参数中加载出来的
![![[Pasted image 20221115142525.png]]](https://img-blog.csdnimg.cn/06fb6419678842a0a09944c995d6f823.png)
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函数族 一个完整的例子:
![![[Pasted image 20221115142624.png]]](https://img-blog.csdnimg.cn/4c5e1ce7e789439ba09910db5e94290a.png)
相关文章:
【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><…...
JavaSec-RCE
简介 RCE(Remote Code Execution),可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景:Groovy代码注入 Groovy是一种基于JVM的动态语言,语法简洁,支持闭包、动态类型和Java互操作性,…...
零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?
一、核心优势:专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发,是一款收费低廉但功能全面的Windows NAS工具,主打“无学习成本部署” 。与其他NAS软件相比,其优势在于: 无需硬件改造:将任意W…...
大型活动交通拥堵治理的视觉算法应用
大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...
【网络安全产品大调研系列】2. 体验漏洞扫描
前言 2023 年漏洞扫描服务市场规模预计为 3.06(十亿美元)。漏洞扫描服务市场行业预计将从 2024 年的 3.48(十亿美元)增长到 2032 年的 9.54(十亿美元)。预测期内漏洞扫描服务市场 CAGR(增长率&…...
定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...
学校招生小程序源码介绍
基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码,专为学校招生场景量身打造,功能实用且操作便捷。 从技术架构来看,ThinkPHP提供稳定可靠的后台服务,FastAdmin加速开发流程,UniApp则保障小程序在多端有良好的兼…...
转转集团旗下首家二手多品类循环仓店“超级转转”开业
6月9日,国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解,“超级…...
Python爬虫(一):爬虫伪装
一、网站防爬机制概述 在当今互联网环境中,具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类: 身份验证机制:直接将未经授权的爬虫阻挡在外反爬技术体系:通过各种技术手段增加爬虫获取数据的难度…...
vue3 定时器-定义全局方法 vue+ts
1.创建ts文件 路径:src/utils/timer.ts 完整代码: import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...
