当前位置: 首页 > news >正文

【Linux】进程程序替换

文章目录

  • 替换原理
    • 站在进程的角度
    • 站在程序的角度
    • 初体验及理解原理
  • 替换函数
    • 函数解释
    • 命名理解
    • exec系列函数与main函数之间的关系
    • 在一个程序中调用我们自己写的程序

替换原理

创建子进程的目的是什么?
->想让子进程执行父进程代码的一部分

执行父进程对应的磁盘代码中的一部分

->想让子进程执行一个全新的程序

让子进程想办法加载磁盘上指定的程序,执行新程序的代码和数据->进程的程序替换

![[进程控制 2023-01-30 15.38.00.excalidraw|700]]

站在进程的角度

没有创建新的进程(PCB尤其是pid是没有变化的),只是程序的替换

站在程序的角度

  1. 这个程序被加载到内存中了->称exec函数为"加载器"
  2. 既然我自己的代码能加载到新的程序,操作系统也是
    1. 先创造进程数据结构(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]]

由虚拟地址空间+页表来保证进程的独立性,一旦有执行流想替换代码或者数据,就发生写时拷贝.->说明写时拷贝在代码区也可以发生的!

但是我们一般不直接在父进程进行程序替换,而是创建新的子进程进行程序替换.
于是我们优化上面的代码:

#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]]

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]]

相关文章:

【Linux】进程程序替换

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

项目构建工具maven的基本配置+idea 中配置 maven

&#x1f451; 博主简介&#xff1a;知名开发工程师 &#x1f463; 出没地点&#xff1a;北京 &#x1f48a; 2023年目标&#xff1a;成为一个大佬 ——————————————————————————————————————————— 版权声明&#xff1a;本文为原创文…...

【解密ChatGPT】:从过去到未来,揭示其发展与变革

&#x1f38a;专栏【ChatGPT】 &#x1f33a;每日一句&#xff1a;天行健,君子以自强不息,地势坤,君子以厚德载物 ⭐欢迎并且感谢大家指出我的问题 文章目录 一、ChatGPT的发展历程 二、ChatGPT的技术原理 三、ChatGPT的应用场景 四、ChatGPT的未来趋势 五、总结 引言:随着…...

系统架构设计】计算机公共基础知识: 5 数学与经济管理

一 运筹方法 1 线性规划 线性规划问题的数学模型通常由线性目标函数、线性约束条件、变量非负条件组成,特点如下: (1)线性规划的可行解域是由一组线性约束条件形成的。 (2)如果存在两个最优解,则连接这两点的线段内所有的点都是最优解,而线段两端延长线上可能会超出…...

Visual Studio 2019光标变成灰色方块问题

文章目录 Visual Studio 2019光标变成灰色方块问题问题描述解决方案 Visual Studio 2019光标变成灰色方块问题 问题描述 单击和双击都无法选中单词&#xff0c;总是选择整行或者是当前光标处的前几个字符一起选中&#xff0c;没有规则&#xff0c;貌似选择单词复制&#xff0…...

C++ http协议POST body raw 字段向服务器发送请求

环境&#xff1a;ubuntu系统c使用http协议不是很方便&#xff0c;通过curl库我们可以很方便使用http协议&#xff0c;由于我的请求方式比较特殊&#xff0c;在网上没有找到相关的资料&#xff0c;之前使用python实现过一版&#xff0c;但是当设备数量超过100台时&#xff0c;程…...

通过migrate命令实现两个redis实例之间的数据迁移

本文适用于将源服务器的redis实例的key的数据迁移到其他服务器的redis实例 一、migrate简介&#xff1a; migrate用于在Redis实例间进行数据迁移&#xff0c;实际上migrate命令是将dump、restore、del三个命令进行组合&#xff0c;从而简化了操作流程。migrate命令具有原子性&…...

Unity 判断两个UI是否相交

今天碰到要判断两个UI是否相交的交互。 尝试了下&#xff0c;发现有两个方法都成功了。 1、使用Collider2D组件 分别创建两个Image组件&#xff0c;并且添加Collider2D组件&#xff0c;其中一个还要添加Rigidbody2D组件&#xff0c;如下图&#xff1a; 然后创建个判断脚本“…...

swoole process 消息通信

swoole文档&#xff1a;Swoole 文档 process子进程和父进程之间通信&#xff0c;依靠监听。子进程和父进程分别做监听。父进程写入信息&#xff0c;子进程监听接收。子进程向父进程写入&#xff0c;调用父进程监听。 子进程向父进程写入信息有两种方式&#xff0c;一种调用wr…...

uniapp跳转方式

UniApp 是一个基于 Vue.js 的跨平台开发框架&#xff0c;可以同时构建 iOS、Android、H5 和小程序等多个平台的应用。在 UniApp 中&#xff0c;我们可以使用以下几种方式进行页面之间的跳转&#xff1a; 使用 uni.navigateTo 方法&#xff1a;该方法可以在当前页面打开新的页面…...

六大排序算法:插入、选择、冒泡、快排、希尔、归并

1、插入排序 解析&#xff1a;第一个元素设定为已经排好序&#xff0c;依次选择后续的元素插入到已经排好序的组内进行排序。 图示&#xff1a; 代码&#xff1a; public static void insertionSort(int[] arr) {int n arr.length;for (int i 1; i < n; i) {int key a…...

短信登录实现(黑马点评为例)

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

【uniapp】签名组件,兼容vue2vue3

网上找了个源码改吧改吧&#xff0c;清除了没用的功能和兼容性&#xff0c;基于uniapp开发的 样子 vue2 使用方法&#xff0c;具体的可以根据业务自行修改 <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以其简单、…...

网络安全和隐私保护技术

一、定义 网络安全和隐私保护技术是指在互联网和其他网络环境中&#xff0c;通过技术手段保护网络系统、网络数据和用户隐私免于受到恶意攻击、非法访问、窃取或滥用。网络安全和隐私保护技术是保护网络安全和用户隐私的重要手段&#xff0c;是保障互联网和其他网络环境正常运…...

保险行业采购管理痛点及解决方案(数智化采购系统)

随着社会发展&#xff0c;个人和企业有了更多的金融保险需求。对于金融保险公司而言&#xff0c;需要在采购合规的基础上&#xff0c;基于数智化能力&#xff0c;让经营变得更加高效和智能。 1、围绕重点领域&#xff0c;业务加速布局。 保险行业结合自身业务经营重点&#x…...

光学仿真 | 仿真推动以人类视觉感知为本的汽车显示设计

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

判断两个对象是否不相等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 云计算的可持续性结语 引言 时光荏苒&#xff0c;科技的飞速发展已经成为当今社会的标志之一。在这个数字化时代&#xff0c;云计算已经成为推动技术革新和生活方式改变的关键…...

Spring Boot项目中通过 Jasypt 对属性文件中的账号密码进行加密

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

浅谈 React Hooks

React Hooks 是 React 16.8 引入的一组 API&#xff0c;用于在函数组件中使用 state 和其他 React 特性&#xff08;例如生命周期方法、context 等&#xff09;。Hooks 通过简洁的函数接口&#xff0c;解决了状态与 UI 的高度解耦&#xff0c;通过函数式编程范式实现更灵活 Rea…...

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…...

UE5 学习系列(三)创建和移动物体

这篇博客是该系列的第三篇&#xff0c;是在之前两篇博客的基础上展开&#xff0c;主要介绍如何在操作界面中创建和拖动物体&#xff0c;这篇博客跟随的视频链接如下&#xff1a; B 站视频&#xff1a;s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略

本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装&#xff1b;只需暴露 19530&#xff08;gRPC&#xff09;与 9091&#xff08;HTTP/WebUI&#xff09;两个端口&#xff0c;即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...

ServerTrust 并非唯一

NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...

HBuilderX安装(uni-app和小程序开发)

下载HBuilderX 访问官方网站&#xff1a;https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本&#xff1a; Windows版&#xff08;推荐下载标准版&#xff09; Windows系统安装步骤 运行安装程序&#xff1a; 双击下载的.exe安装文件 如果出现安全提示&…...

VTK如何让部分单位不可见

最近遇到一个需求&#xff0c;需要让一个vtkDataSet中的部分单元不可见&#xff0c;查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行&#xff0c;是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示&#xff0c;主要是最后一个参数&#xff0c;透明度…...

零基础设计模式——行为型模式 - 责任链模式

第四部分&#xff1a;行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习&#xff01;行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想&#xff1a;使多个对象都有机会处…...

前端开发面试题总结-JavaScript篇(一)

文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包&#xff08;Closure&#xff09;&#xff1f;闭包有什么应用场景和潜在问题&#xff1f;2.解释 JavaScript 的作用域链&#xff08;Scope Chain&#xff09; 二、原型与继承3.原型链是什么&#xff1f;如何实现继承&a…...

Unit 1 深度强化学习简介

Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库&#xff0c;例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体&#xff0c;比如 SnowballFight、Huggy the Do…...