Linux:进程控制(二.详细讲解进程程序替换)
上次讲了:Linux:进程地址空间、进程控制(一.进程创建、进程终止、进程等待)
文章目录
- 1.进程程序替换
- 1.1概念
- 1.2原理
- 1.3使用一个`exec` 系列函数
- execl()函数
- 结论与细节
- 2.多进程时的程序替换
- 3.其他几个exec系列函数
- 也可以调用其他语言的程序
- 想要生成两个可执行文件的makefile
1.进程程序替换
之前我们进行的程序演示里,都只能运行自己的代码。那我们怎么样才能执行其他程序的代码呢?(例如在程序里使用ls之类的指令)就可以使用进程程序替换,一开始我们先只看单进程的情况。后面在引入多进程的情况
1.1概念
进程程序替换是指在运行过程中将一个进程的地址空间中的代码、数据和堆栈等内容完全替换为另一个程序的代码、数据和堆栈的过程。这个过程通常是由操作系统提供的 exec
系列函数来实现的:
-
地址空间替换:进程的地址空间是指进程可以访问的内存范围。通过地址空间替换,进程可以在运行时动态地加载并执行不同的程序,从而实现灵活的程序执行和管理。
-
exec
函数族:exec
函数族是一组系统调用,用于执行程序替换操作。这些函数包括execl
,execv
,execle
,execve
等,它们允许以不同的方式传递参数给新程序,并执行地址空间替换。我们要改变内存,那肯定是要调用系统调用接口的,这些函数会封装相应的接口
-
程序入口点:新程序的入口点是程序中的起始执行位置,通常是
main
函数或其他指定的入口函数。替换完成后,控制权将转移到程序入口点,开始执行新程序的代码。
1.2原理
- 当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换
- 替换完成后,控制权将转移到新程序的入口点,开始执行新程序的代码。
1.3使用一个exec
系列函数
execl()函数
execl函数是Linux系统中用于执行新程序的函数之一,它属于exec函数族的一部分。这个函数的作用是在当前进程的上下文中启动一个新的程序,并替换当前进程的映像为新的程序映像。调用execl函数后,当前进程将停止执行,并由新的程序开始执行。
#include<unistd.h>
int execl(const char *path, const char *arg0, ... /* (char *) NULL */);
参数说明:
path
:要执行的程序的路径。arg0
:新程序的参数列表的开始,通常这会是新程序的名称(尽管这不是强制的,但它通常用于错误消息和程序内部)。...
:一个可变参数列表(参数的数量不固定),新程序的参数列表,必须以NULL结尾。
execl函数会根据提供的路径
path
找到并执行相应的程序,同时将arg0
及其后面的参数作为新程序的命令行参数传递。注意,参数列表必须以NULL结尾,这是告诉execl参数列表结束的标志。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{printf("I'm a process, pid: %d\n", getpid());printf("execl begin...\n");int a=execl("/usr/bin/ls", "ls", "-a", "-l", NULL);printf("execl end...\n");return 0;
}
如果execl函数调用成功,那么它实际上不会返回,因为当前进程的映像已经被新程序替换。如果调用失败,它会返回-1,并设置全局变量errno
以指示错误原因。常见的错误原因可能包括文件未找到、权限不足等。
execl函数和其他exec函数一样,不会创建新的进程。它们只是在当前进程的上下文中启动另一个程序。
因此,调用execl前后,进程的ID(PID)不会改变。同时,由于execl会替换整个进程映像,所以在调用execl之前,通常需要确保当前进程的所有打开的文件描述符、内存分配等都被适当地处理或释放,因为这些资源不会被新程序继承。
结论与细节
-
程序替换一旦成功,
exec
后面的代码不在执行。因为被替换掉了,这也是什么代码没有输出execl end
的原因了 -
exec
函数调用成功,那么它实际上不会有返回值;调用失败,它会返回-1 -
exec
函数不会创建新的进程。它们只是在当前进程的上下文中启动另一个程序 -
创建一个进程。我们是先创建PCB、地址空间、页表等再先把程序加载到内存
先加载的话,页表都没办法映射的
-
程序替换的本质就是加载 (可以看成一个加载器),有替换就是替换,没有就是程序加载
程序替换的本质是程序加载,因为在执行
exec
函数时,操作系统会加载新程序的可执行文件,并将其代码、数据和堆栈等部分加载到进程的地址空间中。这个过程涉及将新程序的内容从磁盘加载到内存中,为进程提供执行所需的资源。因此,虽然我们常说是“程序替换”,但实际上更准确地说是将新程序加载到内存中,替换掉原有的程序,以实现进程的功能切换和更新。 -
程序运行要加载到内存;为什么?冯诺依曼体系规定;如何加载的呢?就是程序替换:程序替换是操作系统的接口,所谓的把磁盘里的数据加载到内存就是把磁盘设备的数据拷贝到内存里。把数据从一个硬件搬到另一个硬件,只有操作系统能做
2.多进程时的程序替换
我们可以创建一个子进程,由子进程来进行程序替换,父进程来等待结果就可以。为什么? 父进程能得到子进程的执行结果
我们知道父进程与子进程映射到同一块代码,那么子进程进行程序替换后,不是会覆盖吗,替换为什么不影响父进程?
进程具有独立性,在进行程序替换时要进行写时拷贝
写时拷贝的本质就是开辟新的空间
shell是如何运行起来一个指令的?
首先创建子进程,shell会waitpid()等待进程结果,子进程会继承shell的代码,但是不影响。子进程进行程序替换,替换为我们输入的指令
int main()
{pid_t id = fork();if (id == 0){printf("I'm a process, pid: %d\n", getpid());printf("execl begin...\n");execl("/usr/bin/ls", "ls", "-a", "-l", NULL);printf("execl end...\n");exit(1);}pid_t rid = waitpid(id, NULL, 0);if (rid > 0){printf("wait successfully\n");}return 0;
}
3.其他几个exec系列函数
-
execl
:该函数允许通过提供可变数量的参数来执行指定的可执行文件。它的原型如下:int execl(const char *path, const char *arg0, ... /*, (char *)0 */);
path
是要执行的可执行文件的路径,arg0
是第一个参数,后续参数都是传递给可执行文件的命令行参数,以NULL
结尾。 -
execlp
:该函数与execl
类似,但是它会在系统的环境变量PATH
指定的目录中查找可执行文件。它的原型如下:int execlp(const char *file, const char *arg0, ... /*, (char *)0 */);
file
是要执行的可执行文件的文件名,arg0
是第一个参数,后续参数都是传递给可执行文件的命令行参数,以NULL
结尾。相比于execl函数,execlp函数的第一个参数能直接写文件名,系统会PATH环境变量里去查找
多的字母
p
:PATH环境变量int main() {pid_t id = fork();if (id == 0){printf("I'm a process, pid: %d\n", getpid());printf("execl begin...\n");execl("ls", "ls", "-a", "-l", NULL);printf("execl end...\n");exit(1);}pid_t rid = waitpid(id, NULL, 0);if (rid > 0){printf("wait successfully\n");}return 0; }
-
execv
:类似于execl
,但是允许传递一个参数数组给被执行的程序。它的原型如下:int execv(const char *path, char *const argv[]);
path
是要执行的可执行文件的路径,argv
是一个以NULL
结尾的参数数组,其中每个元素都是一个字符串,表示命令行参数。相比于exec多个字母
v
:代表vectorint main() {pid_t id = fork();if (id == 0){printf("I'm a process, pid: %d\n", getpid());printf("execl begin...\n");char* argv[] = { "ls","-a","-l",NULL};execv("/usr/bin/ls",argv);printf("execl end...\n");exit(1);}pid_t rid = waitpid(id, NULL, 0);if (rid > 0){printf("wait successfully\n");}return 0; }
-
execvp
:类似于execv
,但是它会在系统的环境变量PATH
指定的目录中查找可执行文件。它的原型如下:
int execvp(const char *file, char *const argv[]);
file
是要执行的可执行文件的文件名,argv
是一个以 NULL
结尾的参数数组,其中每个元素都是一个字符串,表示命令行参数。
既有字母
p
又有v
,结合上面那两种就行
-
execle
:函数与execl
函数类似,但允许在启动新程序时传递额外的环境变量。它的原型如下:int execle(const char *path, const char *arg, ..., char *const envp[]);
path
是要执行的可执行文件的路径,arg
是要传递给新程序的命令行参数,后面的参数是额外的环境变量,以NULL
结尾。
进程程序替换不会替换环境变量的
想要子进程继承全部的环境变量,不用管,直接就能拿到
单纯新增环境变量,在父进程里使用
putenv()
函数,会影响子进程
putenv
是 C 语言中的一个库函数,它定义在<stdlib.h>
头文件中。这个函数用于将字符串添加到环境变量中,或者修改已经存在的环境变量的值。int putenv(const char *string);
- 使用全新的环境变量,就使用
execle()
函数,那么替换后的代码切换后的环境变量就只是我们传入的表里的内容
也可以调用其他语言的程序
code.c里:
int main()
{char* const env[] = {(char*)"first",(char*)"second",NULL };pid_t id = fork();if (id == 0){printf("I'm a process, pid: %d\n", getpid());printf("execl begin...\n");execle("./mytest", "mytest", NULL, env)printf("execl end...\n");exit(1);}pid_t rid = waitpid(id, NULL, 0);if (rid > 0){printf("wait successfully\n");}return 0;
}
test.cpp里:
#include <iostream>
#include <unistd.h>using namespace std;int main()
{for (int i = 0; environ[i]; i++){printf("env[%d]: %s\n", i, environ[i]);}cout << "This is C++" << endl;return 0;
}
当然我们也能传系统环境变量,但是没必要,这样的话直接默认就行
execle("./mytest", "mytest", NULL, environ)//传入这个全局变量
想要生成两个可执行文件的makefile
.PHONY:all
all:mycode mytestmycode:code.cgcc -o $@ $^
mytest:test.cppg++ -o $@ $^ -std=c++11
.PHONY:clean
clean:rm -f mycode mytest
.PHONY
:声明一个或多个目标是伪目标(phony targets)通过声明伪目标,你可以确保make
总是执行相应的命令,而不会因为同名的文件或目录的存在而跳过这些命令- 运行
make
命令时(没有指定具体目标),make
会首先查找Makefile
中的第一个目标,并尝试构建它。在这个过程中,make
会检查该目标的所有依赖项,并递归地处理这些依赖项,直到所有必要的依赖项都被构建或确认为是最新的 - 当
make
工具被调用以构建某个目标时,它会检查该目标的所有依赖项,并根据需要构建这些依赖项。然而,对于clean
这样的伪目标,它并没有列出任何依赖项,因此其他目标的构建状态不会影响clean
的执行
今天就到这里啦,感谢大家支持
相关文章:

Linux:进程控制(二.详细讲解进程程序替换)
上次讲了:Linux:进程地址空间、进程控制(一.进程创建、进程终止、进程等待) 文章目录 1.进程程序替换1.1概念1.2原理1.3使用一个exec 系列函数execl()函数结论与细节 2.多进程时的程序替换3.其他几个exec系…...

Elasticsearch8.13.4版本的Docker启动关闭HTTPS
博主环境是: 开发环境:SpringbootElasticSearch客户端对应的starter 2.6.3版本 maven配置 <!-- ElasticSearch --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-elas…...
linux 之dma_buf (8)- ION简化版本
一、前言 我们学习了如何使用 alloc_page() 方式来分配内存,但是该驱动只能分配1个PAGE_SIZE。本篇我们将在上一篇的基础上,实现一个简化版的ION驱动,以此来实现任意 size 大小的内存分配。 二、准备 为了和 kernel 标准 ion 驱动兼容&…...

⌈ 传知代码 ⌋ 高速公路车辆速度检测软件
💛前情提要💛 本文是传知代码平台中的相关前沿知识与技术的分享~ 接下来我们即将进入一个全新的空间,对技术有一个全新的视角~ 本文所涉及所有资源均在传知代码平台可获取 以下的内容一定会让你对AI 赋能时代有一个颠覆性的认识哦&#x…...

scrapy 整合 mitm
1.mitm 是什么 MITMproxy 是一个开源的中间人代理,常用于网络流量的拦截、查看和修改。 2.scrapy 整合 mitm步骤 2.1 安装mitm PS F:\studyScrapy\itcastScrapy> pip install mitmproxy2.2 在settings 中配置下载器中间件 # settings.pyDOWNLOADER_MIDDLEWARES…...
linux大文件切割
在一些小众的场景下出现的大文件无法一次性传输 当然我遇到了 ,work中6G镜像文件无法一次性刻盘到4.7G大小的盘 split split -b 3G 源大文件 目标文件 #安静等待会生成目标文件名a、b、c......-b <大小>:指定每个输出文件的大小,单位为…...

图像分割模型LViT-- (Language meets Vision Transformer)
参考:LViT:语言与视觉Transformer在医学图像分割-CSDN博客 背景 标注成本过高而无法获得足够高质量标记数据医学文本注释被纳入以弥补图像数据的质量缺陷半监督学习:引导生成质量提高的伪标签医学图像中不同区域之间的边界往往是模糊的&…...

CANDela studio之CDDT与CDD
CDDT有更高的权限,作为模板规范CDD文件。 CDD可修改的内容比CDDT少。 CDDT根据诊断协议提供诊断格式,主要就是分类服务和定义服务,一般是OEM释放,然后由供应商细化成自己零部件的CDD文件。 在这里举个例子,OEM在CDDT…...
Java中的注解(Annotation)是什么?它们有什么用途?
技术难点 在Java中,注解(Annotation)是一种元数据(metadata)的形式,用于为Java代码(类、方法、变量、参数和包等)提供额外的信息。这些信息在运行时可以通过反射机制进行读取和处理…...

【CUDA】Nsight profile驱动的CUDA优化
前置准备 安装NVIDIA Nsight Compute。 安装好后选择使用管理员权限启动下载官方 Demo 代码官方博客Shuffle warp 1. 任务介绍及CPU版本 1.1 任务介绍 任务理解: 有一个 L x M 的矩阵 M 1 M_1 M1 对其每行取平均值 得到 V 1 ∈ R L 1 V_1 \in \mathbb{R}^{…...
字符串的拼接
字符串拼接方式1 之前的算术运算符,只是用来数值类型进行数学运算的,而string不存在算术运算符不能计算,但是可以通过号来进行字符串拼接。 string str "123"; //用进行拼接 str str "456"; Console.WriteLine(str)…...

HIVE3.1.3+ZK+Kerberos+Ranger2.4.0高可用集群部署
目录 一、集群规划 二、介质下载 三、基础环境准备 1、解压文件 2、配置环境变量 四、配置zookeeper 1、创建主体 2、修改zoo.cfg 3、新增jaas.conf 4、新增java.env 5、重启ZK 6、验证ZK 五、配置元数据库 六、安装HIVE 1、创建Hiver的kerberso主体 2…...

Android ANR Trace日志阅读分析技巧
什么是Trace日志 Trace日志是指ANR目录下的一份txt文件 adb pull /data/anr/traces.txt Trace日志有什么用 分析应用ANR无响应的问题, Trace怎么用 Cmd line: com.xx ABI: arm Build type: optimized Zygote loaded classes3682 post zygote classes3750 Intern…...
前端Ajax、Axios和Fetch的用法和区别笔记
前端 JavaScript 开发中,进行 HTTP 请求的三种主要方式是 Ajax、Axios 和 Fetch。这三种方式各有优缺点,并且适用于不同的场景。在合适的业务场景下使用,以下是它们的区别和使用举例。 1. Ajax Ajax(Asynchronous JavaScript an…...
Android的Framework(TODO)
(TODO)...
牛客小白月赛94 EF题解
题目描述 注:此版本为本题的hard(困难版),与easy(简单版)唯一的不同之处只有数据范围。 小苯有一个容量为 k 的背包,现在有 n 个物品,每个物品有一个体积 v 和价值 w࿰…...
大数据开发面试题【Flink篇】
148、flink架构 flink是一个框架和分布式处理引擎,用于对无界和有界数据流进行有状态计算 特点: 高吞吐和低延迟:每秒数百万个事件,毫秒级延迟 结果的准确性:提供了事件时间和处理时间语义,提供结果的一致…...
Java技术深度解析:高级面试问题与精粹答案(二)
Java 面试问题及答案 1. 什么是Java的垃圾回收机制?它是如何工作的? 答案: Java的垃圾回收机制(Garbage Collection,GC)是Java运行时环境(JRE)中的一个功能,用于自动管…...
算数运算符
算术运算符是用于数值类型变量计算的运算符。 它的返回结果是数值。 赋值符号 关键知识点:先看右侧,再看左侧,把右侧的值赋值给左侧的变量。 附上代码: string myName "唐唐"; int myAge 18; float myHeight 177.5…...

闲话 .NET(3):.NET Framework 的缺点
前言 2016 年,微软正式推出 .NET Core 1.0,并在 2019 年全面停止 .NET Framework 的更新。 .NET Core 并不是 .NET Framework 的升级版,而是一个从头开始开发的全新平台,一个跟 .NET Framework 截然不同的开源技术框架。 微软为…...
零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?
一、核心优势:专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发,是一款收费低廉但功能全面的Windows NAS工具,主打“无学习成本部署” 。与其他NAS软件相比,其优势在于: 无需硬件改造:将任意W…...
Vue记事本应用实现教程
文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展:显示创建时间8. 功能扩展:记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...

无法与IP建立连接,未能下载VSCode服务器
如题,在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈,发现是VSCode版本自动更新惹的祸!!! 在VSCode的帮助->关于这里发现前几天VSCode自动更新了,我的版本号变成了1.100.3 才导致了远程连接出…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...

《通信之道——从微积分到 5G》读书总结
第1章 绪 论 1.1 这是一本什么样的书 通信技术,说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号(调制) 把信息从信号中抽取出来&am…...
生成 Git SSH 证书
🔑 1. 生成 SSH 密钥对 在终端(Windows 使用 Git Bash,Mac/Linux 使用 Terminal)执行命令: ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" 参数说明: -t rsa&#x…...

C++ 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”
目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...
今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存
文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...
Java 二维码
Java 二维码 **技术:**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...