【Linux】进程控制-----进程替换
目录
一、为什么要进行进程替换:
二、进程替换的原理:
三、exec家族:
1、execl:
2、execlp:
3、execv:
4、execvp:
5、execle和execve
编辑
putenv:
一、为什么要进行进程替换:
当父进程创建子进程之后,这两个进程后面的代码是共享的,但是更多的场景下是给子进程执行分配任务,让子进程执行它自己的任务,这个时候就需要进程替换了,让别的进程替换子进程,但是这个替换并没有创建PCB
程序替换的目的是让子进程帮我们执行特定任务
二、进程替换的原理:
单进程替换图解:

父进程创建子进程,然后子进程进行程序替换图解:
替换前:

如上,父进程创建子进程,然后子进程的代码和数据继承父进程的代码和数据的,然后即将发生替换的程序加载到内存中,页表中子进程的物理内存映射变大或变小为新进程的大小,
替换后:

替换后会发生写时拷贝,不仅仅是数据,代码也会,
当子进程需要执行不同的代码时,操作系统会将父进程的代码和数据拷贝一份,然后将新的程序加载到内存中,重新建立子进程的页表,并更新子进程页表中的映射关系。这一过程确保了父子进程的代码分离,使得它们互不干扰,写时拷贝技术保证了父子进程在没有对代码和数据进行修改时共享相同的物理内存,一旦发生写操作,就会为子进程分配新的物理空间,并拷贝数据,从而实现进程间的独立性,因此,当子进程进行程序替换时,父进程不受影响,因为替换过程中发生的写时拷贝确保了子进程的独立性
接下来理解两个问题:
进程程序替换时,有没有创建新的进程?
当进程程序替换后,子进程的PCB,进程地址空间和页表都没有变,只是物理内存的数据和代码发生写时拷贝,但是这个子进程的pid并没有改变,也就是说,程序替换不创建新进程,只是进行进程的代码和数据的切换,
CPU是如何获取程序的入口地址的?
首先,在Linux中形成的可执行程序并不是杂乱无章的,是有格式的,Linux中这个格式成为ELF,这个可执行程序的最开始有可执行程序的表头,这个表里面有可执行程序的入口地址,我们之前了解到程序加载是惰性加载的,在程序加载到内存中可以先不把它的代码和数据加载到内存中,但一定要把表头加载到内存中,
在这个表里面有可执行程序的入口地址,这样CPU就能够从表中读取到程序的入口地址了
三、exec家族:
子进程替换父进程是用exec系列的函数来进行程序替换


如上,虽然有7个但是只有execve是系统调用接口,其他的都是调用这个接口进行了一次封装,
1、execl:
理解:

参数1:新替换程序的路径,(./当前路径,或者绝对路径如/usr/bin/ls)
参数2:新替换程序的名称,如ls,自己写的程序名
参数3:新替换程序的选项,如-a,-l,但是最后一个要是NULL表示结束
。。。:这是表示可变参数列表,可以传递不一样的参数个数
返回值:替换失败返回-1,替换成功的话子进程后面程序就不执行了,所以返回值也就没意义了
例如我要把子进程替换为ls进程:
execl("/usr/bin/ls","ls","-a","-l",NULL);
在代码中的使用:
int main()
{pid_t id = fork();if(id < 0){printf("进程创建失败\n");exit(1);}else if(id == 0){//子进程printf("子进程替换进程前\n");int ret = execl("/usr/bin/ls","ls","-a","-l",NULL);if(ret == -1){printf("进程替换失败\n");}printf("子进程替换进程后\n");exit(1);}else{//父进程printf("父进程正常执行\n");pid_t ret = waitpid(id,NULL,0);if(ret == id){printf("等待成功\n");}}return 0;
}

如上为执行结果:
可以看到execl之后的代码没有被执行,因为这些代码是属于原来子进程的代码,在execl之后这些代码就被替换了,原本的代码也就肯定不会执行
为什么,父进程能够等待得到已经被替换的进程呢?
这是因为虽然进程被替换了,但是并没有创建新的进程,替换后这个进程依然是父进程的子进程,pid也没有变,那么就可以正常等待
2、execlp:
理解:

参数1:这个和execl不同的是这个就只需要传执行的文件名就行了,路径会自动在环境变量中找
参数2:新替换程序的名称,如ls,自己写的程序名
参数3:新替换程序的选项,如-a,-l,但是最后一个要是NULL表示结束
。。。:这是表示可变参数列表,可以传递不一样的参数个数
返回值:替换失败返回-1,替换成功的话子进程后面程序就不执行了,所以返回值也就没意义了
这个就可以看做execl的升级版,也就是不需要手动传路径,会自动在环境变量中找
例如我要把子进程替换为ls进程:
execlp("ls","ls","-a","-l",NULL);
注意:
这虽然有两个ls,但是含义是不同的第一个参数是要执行程序的名字,第二个参数是可变参数列表
在代码中的使用:
int main()
{pid_t id = fork();if(id < 0){printf("进程创建失败\n");exit(1);}else if(id == 0){//子进程printf("子进程替换进程前\n");int ret = execlp("ls","ls","-a","-l",NULL);if(ret == -1){printf("进程替换失败\n");}printf("子进程替换进程后\n");exit(1);}else{//父进程printf("父进程正常执行\n");pid_t ret = waitpid(id,NULL,0);if(ret == id){printf("等待成功\n");}}return 0;
}
执行结果和execl是一样的
3、execv:
理解:
![]()
参数1:新替换程序的路径,(./当前路径,或者绝对路径如/usr/bin/ls)
参数2:这个是传一个 指针数组,里面成员就是类似于execl后面的参数
例图:(差不多就是下面这个意思)

int main()
{pid_t id = fork();if(id < 0){printf("进程创建失败\n");exit(1);}else if(id == 0){//子进程printf("子进程替换进程前\n");char* const myargv[] = { "ls" , "-a" , "-l" , NULL };int ret = execv("/usr/bin/ls",myargv);if(ret == -1){printf("进程替换失败\n");}printf("子进程替换进程后\n");exit(1);}else{//父进程printf("父进程正常执行\n");pid_t ret = waitpid(id,NULL,0);if(ret == id){printf("等待成功\n");}}return 0;
}
执行结果和execl是一样的
4、execvp:
![]()
char* const myargv[] = { "/usr/bin/ls" , "-a" , "-l" , NULL };int ret = execv("/usr/bin/ls",myargv);
这个和execl升级成execlp是一样的,就只是从自己在第一个参数中给路径变为只需传执行的文件名就行了,路径会自动在环境变量中找
5、execle和execve
理解:
参数1,参数2和返回值是一样的,就是多了一个环境参数,envp就是你自己设置的环境变量
putenv:
首先了解一下putenv函数:
这就是一个创建环境变量
putenv("环境变量=数值")
作用:在当前进程下创建一个环境变量并继承给子进程,并不会影响父进程
exec系列带e的函数中,就可以传环境变量:
int main()
{putenv("MYENV=6666666");pid_t id = fork();if(id < 0){printf("进程创建失败\n");exit(1);}else if(id == 0){//子进程printf("子进程替换进程前\n");execlp("./other","other",NULL); printf("子进程替换进程后\n");exit(1);}else{printf("父进程正常执行\n");pid_t ret = waitpid(id,NULL,0);if(ret == id){printf("等待成功\n");}}return 0;
}

上面是bash的环境变量,下面是上述程序的运行结果,所以可以通过putenv来增加环境变量

也可以使用自己的环境变量,这个时候就需要使用execle或者execve了:
int main()
{pid_t id = fork();if(id < 0){printf("进程创建失败\n");exit(1);}else if(id == 0){//子进程printf("子进程替换进程前\n");char* const myenv[] = {"MYENV1=123","MYENV2=456","MYENV3=789",NULL};char* const myargv[] = {"other","-a","-b",NULL};execve("./other",myargv,myenv); printf("子进程替换进程后\n");exit(1);}else{//父进程printf("父进程正常执行\n");pid_t ret = waitpid(id,NULL,0);if(ret == id){printf("等待成功\n");}}return 0;
}
如上,其中自己所写的other进程就是打印命令行参数和环境变量
int main(int argc,char* argv[],char* env[])
{cout<<"命令行参数"<<endl;for(int i = 0;argv[i];i++){cout<<i<<":"<<argv[i]<<endl;}cout<<"环境变量表"<<endl;for(int i = 0;env[i];i++){cout<<i<<":"<<env[i]<<endl;}return 0;
}
这样,在调用之后就可以打印出自己所传的命令行参数,和自己建立的环境变量表

想要理解好带e的函数,就需要先理解好exec在bash中的作用:
首先,在bash中,所有进程都是bash的子进程,那么这些子进程的命令行参数是从哪儿来的呢?就是通过exec系列的函数进行传参的,把第二个参数的选项作为命令行参数传给第一个参数指向的可执行性程序,所以在Linux中,当进程启动的时候就是父进程创建一个子进程,然后这个子进程的数据和代码替换为所执行的命令,exec系列的函数就是起到了类似于加载的作用
exec也可以加载脚本
比如下面写一个Python的脚本语言,

接着在子进程中调用脚本语言:

这样就能够运行脚本语言

所以,尽管Python和cpp是两种语言,但是竟然可以进行跨语言调用,这是为什么呢?
其实所有语言运行起来都是进程,只要是进程就可以被调用,所以,只要这个语言运行起来变成进程,那么就可以使用exec系列函数进行调用!!!
相关文章:
【Linux】进程控制-----进程替换
目录 一、为什么要进行进程替换: 二、进程替换的原理: 三、exec家族: 1、execl: 2、execlp: 3、execv: 4、execvp: 5、execle和execve 编辑 putenv: 一、为什么要进行进程…...
安装SQL Server 2022提示需要Microsoft .NET Framework 4.7.2 或更高版本
安装SQL Server 2022提示需要Microsoft .NET Framework 4.7.2 或更高版本。 原因是:当前操作系统版本为Windows Server 2016 Standard版本,其自带的Microsoft .NET Framework 版本为4.6太低,不满足要求。 根据报错的提示,点击链接…...
使用ECharts创建带百分比标注的环形图
在数据可视化领域,环形图是一种非常有效的图表类型,它能够清晰地展示各部分与整体的关系。今天,我们将通过ECharts来创建一个带百分比标注的环形图,并详细解释如何实现这一效果。 1. 数据准备 首先,我们定义了一些基础…...
学习threejs,设置envMap环境贴图创建反光效果
👨⚕️ 主页: gis分享者 👨⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨⚕️ 收录于专栏:threejs gis工程师 文章目录 一、🍀前言1.1 ☘️THREE.CubeTextureLoader 立…...
go语言里的mkdir mkdirall有什么区别?
在Go语言中,os.Mkdir 和 os.MkdirAll 都是用来创建目录的函数,但它们之间存在一些关键的区别。 ### os.Mkdir - **功能**:os.Mkdir 用于创建一个单一的目录。如果该目录已经存在,则会返回一个错误。 - **参数**: - na…...
使用Python OpenCV实现图像形状检测
目录 一、环境准备 二、读取和预处理图像 读取图像 灰度化 滤波去噪 三、边缘检测 四、查找轮廓 五、绘制轮廓 六、形状分类 七、显示结果 八、完整代码示例 九、总结 图像形状检测是计算机视觉领域中的一项关键技术,广泛应用于工业自动化、机器人视觉、医学图像处…...
继上一篇,设置弹框次数以及自适应图片弹框,部分机型(vivo)老手机不显示的问题
上一篇写的本来测试好多型号都无事, 今天下午公司的战斗机vivo横空冒出… 晕 弹框直接显示都出不来了,现在还有用这种老的机型的,但是没办法咯~ 前端遇到这种兼容性的问题就要勇于解决 主要解决了这几点: // 添加图片加载事件 <imgv-if"imageUrl":src"image…...
基于RISC-V 的代理内核实验(使用ub虚拟机安装基本环境)
1.安装支撑软件 第一步,安装依赖库 RISC-V交叉编译器的执行仍然需要一些本地支撑软件包,可使用以下命令安装: $ sudo apt-get install autoconf automake autotools-dev curl libmpc-dev libmpfr-dev libgmp-dev gawk build-essential bis…...
【MMKV】HarmonyOS中的优秀轻量化存储方式
MMKV 引言 在移动应用开发的世界里,数据存储和管理是至关重要的一环。随着技术的不断进步,开发者们对于高性能、轻量级、易用的数据存储解决方案的需求日益增长。MMKV(Memory Mapped Key-Value)正是这样一个开源的高性能key-val…...
docker安装hadoop环境
一、使用docker搭建基础镜像 1、拉取centos系统镜像 # 我这里使用centos7为例子 docker pull centos:7 2、创建一个dockerfiler文件,用来构建自定义一个有ssh功能的centos镜像 # 基础镜像 FROM centos:7 # 作者 #MAINTAINER hadoop ADD Centos-7.repo /etc/yum.re…...
开源多媒体处理工具ffmpeg是什么?如何安装?使用ffmpeg将M3U8格式转换为MP4
目录 一、FFmpeg是什么二、安装FFmpeg(windows)三、将M3U8格式转换为MP4格式 一、FFmpeg是什么 FFmpeg是一款非常强大的开源多媒体处理工具,它几乎可以处理所有类型的视频、音频、字幕以及相关的元数据。 FFmpeg的主要用途包括但不限于&…...
算法刷题Day5: BM52 数组中只出现一次的两个数字
描述: 一个整型数组里除了两个数字只出现一次,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。 要求:空间复杂度 O(1),时间复杂度O(n)。 题目传送门 is here 思路: 方法一:最简单的思路就…...
55 基于单片机的方波频率可调
目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 采用STC89C52单片机最小系统,设计DAC0832、放大器、与示波器显示方波,四位数码管显示频率,两个按键可调。 二、硬件资源 基于KEIL5编写C代码,PROT…...
23.useUnload
在 Web 应用开发中,处理页面卸载(unload)事件是一个重要但常常被忽视的方面。无论是提醒用户保存未保存的更改,还是执行一些清理操作,都需要在用户即将离开页面时进行处理。useUnload 钩子提供了一种简洁的方式来在 React 组件中处理 beforeunload 事件,使得在用户试图关…...
linux环境搭建
1、**连接外网** ssh在192.168.4.x上运行sudo ip link set ens160 down ssh切换到192.168.3.x(外网ip),运行sudo ip route add default via 192.168.2.1 dev ens192 onlink //连接外网 使用完外网后 ssh在192.168.3.x上运行sudo ip link set ens160 up ssh在1…...
《C++与生物医学的智能融合:医疗变革新引擎》
在当今科技飞速发展的时代,人工智能正以前所未有的深度和广度渗透到各个领域,为传统行业带来革新与突破。其中,将 C与生物学、医学等领域知识相结合,开发用于处理生物医学数据、辅助疾病诊断和治疗的人工智能应用,成为…...
Matlab 绘制雷达图像完全案例和官方教程(亲测)
首先上官方教程链接 polarplothttps://ww2.mathworks.cn/help/matlab/ref/polarplot.html 上实例 % 定义角度向量和径向向量 theta linspace(0, 2*pi, 5); r1 [1, 2, 1.5, 2.5, 1]; r2 [2, 1, 2.5, 1.5, 2];% 绘制两个雷达图 polarplot(theta, r1, r-, LineWidth, 2); hold …...
Lua的环境与热更
一、global_State,lua_State与G表 Lua支持多线程环境,使用 lua_State 结构来表示一个独立的 Lua 线程(或协程)。每个线程都需要一个独立的全局环境。而lua_State 中的l_G指针,指向一个global_State结构,这个就是我们常…...
HTML CSS JS基础考试题与答案
一、选择题(2分/题) 1.下面标签中,用来显示段落的标签是( d )。 A、<h1> B、<br /> C、<img /> D、<p> 2. 网页中的图片文件位于html文件的下一级文件夹img中,…...
若依解析(一)登录认证流程
JWTSpringSecurity 6.X 实现登录 JWT token只包含uuid ,token 解析uuid,然后某个常量加UUID 从Redis缓存查询用户信息 流程图如下 感谢若依,感谢开源,能有这么好系统供我学习。 设计数据库,部门表,用户表,…...
TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
React Native在HarmonyOS 5.0阅读类应用开发中的实践
一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强,React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 (1)使用React Native…...
对WWDC 2025 Keynote 内容的预测
借助我们以往对苹果公司发展路径的深入研究经验,以及大语言模型的分析能力,我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际,我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测,聊作存档。等到明…...
Nuxt.js 中的路由配置详解
Nuxt.js 通过其内置的路由系统简化了应用的路由配置,使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...
C++ 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
vue3+vite项目中使用.env文件环境变量方法
vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量,这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...
优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...
鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南
1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发,使用DevEco Studio作为开发工具,采用Java语言实现,包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...
关键领域软件测试的突围之路:如何破解安全与效率的平衡难题
在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件,这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下,实现高效测试与快速迭代?这一命题正考验着…...
RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)
RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发,后来由Pivotal Software Inc.(现为VMware子公司)接管。RabbitMQ 是一个开源的消息代理和队列服务器,用 Erlang 语言编写。广泛应用于各种分布…...


