六十天Linux从0到项目搭建(第十五天)(程序替换、exec流程示意图、核心特性)
1 为什么要有程序替换?
程序替换(Process Replacement)是操作系统中一个关键机制,它的核心目的是:让一个正在运行的进程(通常是子进程)停止执行当前代码,转而加载并执行一个全新的程序。
核心需求:
-
子进程需要执行不同的任务
-
父进程创建子进程后,子进程默认会执行父进程的代码(
fork()后的代码)。 -
但如果子进程需要执行 完全不同的程序(如
ls、gcc等),就需要程序替换。
-
-
避免重复创建进程的开销
-
如果每次运行新程序都先
fork()再exec(),可以复用已有的进程结构(如PID、文件描述符等),比直接创建新进程更高效。
-
-
实现灵活的进程管理
-
比如 Shell 执行命令时:
$ ls -l # Shell fork() + exec("ls") 来运行 `ls`,而不是自己实现 `ls` 的功能
-
2. 创建子进程的目的是什么?
(1)让子进程执行父进程的一部分代码
-
默认情况下,
fork()创建的子进程会 继承父进程的代码和数据,继续执行fork()之后的逻辑。 -
适用场景:
-
多进程并行计算(如分治算法)。
-
父进程和子进程协作完成同一任务(如网络服务器处理请求)。
-
(2)让子进程执行一个全新的程序
-
通过 程序替换(
exec系列函数),子进程可以加载另一个程序的代码和数据,完全替换自己。 -
适用场景:
-
Shell 运行外部命令(如
ls、grep)。 -
动态加载插件或子模块(如游戏引擎加载新关卡逻辑)。
-
3. 程序替换的本质
程序替换通过 exec 系列函数 实现,它的核心行为是:
-
替换当前进程的代码和数据
-
销毁当前进程的 代码段、数据段、堆栈,重新加载目标程序的代码和数据。
-
注意:
PID、文件描述符、信号处理等 进程属性不变。
-
-
不创建新进程
-
exec只是 替换当前进程的内容,不会像fork()那样新建进程。
-
-
成功后永不返回
-
如果
exec成功,原程序的代码已被覆盖,exec之后的代码不会执行。 -
只有失败时(如目标程序不存在),
exec才会返回-1。
-
4. 程序替换的典型用法
(1)Shell 运行命令
pid_t pid = fork();
if (pid == 0) {// 子进程替换为 `ls -l`execlp("ls", "ls", "-l", NULL); // 如果成功,不会执行后面的代码perror("exec failed"); // 只有 exec 失败时才会执行exit(1);
} else {wait(NULL); // 父进程等待子进程结束
}
(2)动态加载程序
// 子进程替换为自定义程序
char *argv[] = {"./my_program", "arg1", "arg2", NULL};
execv(argv[0], argv);
5. 程序替换 vs. 普通进程创建
| 场景 | fork() | fork() + exec() |
|---|---|---|
| 子进程执行的内容 | 父进程的代码 | 全新的程序代码 |
| 进程 PID | 新创建的子进程 | 复用子进程的 PID |
| 典型用途 | 多进程并行计算 | Shell 执行命令、动态加载插件 |
6. 为什么不用 fork() 直接创建新程序?
-
效率问题:
fork()只复制进程结构,而exec可以复用这个结构,避免完全新建进程的开销。 -
灵活性:
父进程可以在fork()后通过exec让子进程执行任意程序,而无需预先绑定。
7. 总结
-
程序替换(
exec) 的目的是让进程 动态切换执行不同的程序,而不是局限于父进程的代码。 -
fork() + exec()是 Linux 运行新程序的标准方式(如 Shell 执行命令)。 -
关键特点:
-
替换代码和数据,但保留
PID、文件描述符等。 -
成功时不返回,失败时返回
-1。
-
示例类比:
-
fork()像 克隆一个自己,默认做同样的事。 -
exec像 灵魂转移,保留身体(进程资源),但彻底换了一个大脑(程序代码)
2 程序替换(Process Replacement)的原理
程序替换是操作系统提供的一种机制,允许一个进程 动态加载并执行另一个全新的程序,而无需创建新的进程。它的核心原理可以分为以下几个部分:
1. 程序替换的核心函数
在 Linux 中,程序替换通过 exec 系列函数 实现,主要包括:
-
execl,execlp,execle(参数列表形式) -
execv,execvp,execvpe(参数数组形式)
示例:
execl("/bin/ls", "ls", "-l", NULL); // 替换为 `ls -l`
execvp("gcc", (char *[]){"gcc", "main.c", "-o", "main", NULL}); // 替换为 `gcc`
2. 程序替换的核心原理
(1)替换进程的代码和数据
-
原进程的代码段(
.text)、数据段(.data、.bss)、堆栈被销毁。 -
新程序的代码和数据从磁盘加载到内存,并映射到当前进程的地址空间。
-
新程序从
main函数开始执行。
(2)保留进程的某些属性
虽然代码和数据被替换,但以下信息仍然保留:
-
进程 ID(PID):仍然是原来的进程,只是内容变了。
-
文件描述符表:已打开的文件(如
stdin、stdout)仍然有效(除非显式设置O_CLOEXEC)。 -
进程优先级、信号处理方式(除非新程序重新设置)。
-
用户 ID、组 ID(除非新程序是
setuid/setgid程序)。
(3)不创建新进程
-
exec不会创建新进程,只是替换当前进程的代码和数据。 -
如果
exec成功,原进程的代码不再执行,控制权交给新程序。 -
如果
exec失败(如目标程序不存在),返回-1,并继续执行原程序。
3. 程序替换的底层机制
(1)内核的处理流程
-
解析目标程序:
-
检查目标程序是否存在,是否有执行权限。
-
读取目标程序的 ELF 头部信息(如代码段、数据段的布局)。
-
-
释放原进程资源:
-
销毁原进程的代码、数据、堆、栈等内存区域。
-
关闭标记了
O_CLOEXEC的文件描述符。
-
-
加载新程序:
-
将新程序的代码段(
.text)、数据段(.data)加载到内存。 -
设置新的堆栈(
stack)和堆(heap)。
-
-
更新进程的页表:
-
建立虚拟地址到物理地址的新映射。
-
-
跳转到新程序的入口点(通常是
_start,最终调用main)。
(2)为什么 exec 成功后不会返回?
-
exec替换了当前进程的所有代码,包括调用exec的代码本身。 -
因此,如果
exec成功,原程序的执行流被彻底覆盖,没有任何代码可以继续执行。 -
只有
exec失败时,才会返回-1,并继续执行原程序。
4. 程序替换的典型应用
(1)Shell 执行命令
$ ls -l # Shell 会 fork() + exec("ls"),而不是自己实现 `ls` 的功能
Shell 的伪代码:
pid_t pid = fork();
if (pid == 0) {// 子进程替换为 `ls`execlp("ls", "ls", "-l", NULL);exit(1); // 只有 exec 失败时才执行
} else {wait(NULL); // 父进程等待子进程结束
}
(2)动态加载插件
// 子进程加载插件
if (fork() == 0) {execv("./plugin.so", args);exit(1);
}
5. 程序替换 vs. 普通进程创建
| 对比项 | fork() | fork() + exec() |
|---|---|---|
| 进程 PID | 创建新进程(新 PID) | 复用子进程的 PID |
| 代码和数据 | 复制父进程的代码和数据 | 完全替换为新程序的代码和数据 |
| 典型用途 | 多进程并行计算 | Shell 执行命令、动态加载插件 |
6. 总结
-
程序替换(
exec)的本质:-
替换当前进程的代码和数据,但保留
PID、文件描述符等属性。 -
不创建新进程,仅复用现有进程的“外壳”。
-
-
关键特点:
-
成功时不返回(原代码被覆盖)。
-
失败时返回
-1(如目标程序不存在)。
-
-
典型应用:
-
Shell 执行外部命令。
-
动态加载插件或子模块。
-
类比:
-
fork()像 克隆自己(相同代码)。 -
exec像 换脑手术(保留身体,换新大脑)。
3 Linux 内核处理 exec 程序替换的流程示意图
用户态
↓
execve("/bin/ls", ["ls", "-l"], envp) // 用户调用 exec 函数
↓
--------------------------------------------------------------
内核态(系统调用入口)
↓
1. 检查权限 & 目标文件是否存在?→ 失败?返回 -1 (ENOENT/EACCES)→ 成功?继续
↓
2. 解析目标文件(ELF 格式)├─ .text (代码段)├─ .data (初始化数据)└─ .bss (未初始化数据)
↓
3. 准备新内存空间├─ 释放原进程的:│ ├─ 代码段│ ├─ 数据段│ └─ 堆栈└─ 加载新程序的:├─ 代码段(只读映射)├─ 数据段(私有映射)└─ 设置新堆栈
↓
4. 更新进程控制块(PCB)├─ 保留:PID、父进程、文件描述符表└─ 重置:信号处理器、计时器
↓
5. 跳转到新程序入口(_start → main)
↓
--------------------------------------------------------------
用户态
↓
新程序开始执行(原进程的代码已完全消失)
关键点说明
-
权限检查阶段
-
确认目标文件存在且可执行
-
检查
setuid/setgid权限位
-
-
ELF 加载阶段
-
通过
mmap建立代码段(只读)和数据段(可写)的映射 -
初始化
.bss段为零页
-
-
资源清理阶段
-
关闭标记了
FD_CLOEXEC的文件描述符 -
保留标准输入/输出/错误流
-
-
执行转移阶段
-
通过修改
rip寄存器跳转到新程序的_start符号 -
完全不会回到原调用点(除非加载失败)
-
典型错误路径
execve("/not/exist", ...)
↓
内核发现文件不存在
↓
返回 -1 (errno=ENOENT)
↓
原进程继续执行(检查返回值)
与 fork() 的对比
fork() 流程: 用户进程 → 复制PCB → 复制页表 → 返回两次exec() 流程: 用户进程 → 销毁内存映射 → 新建映射 → 永不返回(成功时)
这个流程体现了 Linux "进程是执行流的容器" 的设计哲学:保持进程外壳(PID、文件描述符等),仅替换内部执行内容。
4 多进程环境下程序替换(exec)的核心特性
1. 程序替换是「整体替换」
(不能局部替换)
原进程内存布局: +-------------------+ | 代码段 | ← 完全被新程序覆盖 | 数据段 | ← 完全被新程序覆盖 | 堆 | ← 被新程序重建 | 栈 | ← 被新程序重建 +-------------------+
-
所有用户空间内容被替换(包括全局变量、函数指针等)
-
内核空间信息保留(PID、文件描述符、信号处理表等)
2. 进程独立性保障
(替换仅影响当前进程)
父进程 (PID=100)
├─ 子进程A (PID=101) → exec("./new_prog") → 变成全新程序
└─ 子进程B (PID=102) → 继续运行原程序
-
写时复制(COW)机制确保:
-
子进程在
exec()前与父进程共享物理页 -
exec()后所有内存页都会触发COW,建立独立映射
-
3. 所有exec系列接口对比
| 接口函数 | 参数传递方式 | 环境变量处理 | 路径搜索规则 |
|---|---|---|---|
execl | 可变参数列表 | 继承父进程环境 | 需绝对/相对路径 |
execle | 可变参数列表 | 自定义环境变量数组 | 需绝对/相对路径 |
execlp | 可变参数列表 | 继承父进程环境 | 自动搜索PATH目录 |
execv | 字符串数组 | 继承父进程环境 | 需绝对/相对路径 |
execvp | 字符串数组 | 继承父进程环境 | 自动搜索PATH目录 |
execvpe | 字符串数组 | 自定义环境变量数组 | 自动搜索PATH目录 |
4. 典型多进程替换场景
// 父进程创建多个工作子进程
for (int i = 0; i < 5; i++) {pid_t pid = fork();if (pid == 0) {// 每个子进程替换不同程序char *env[] = {"MY_ID=i", NULL};execle("./worker", "worker", NULL, env);_exit(1); // 只有exec失败时执行}
}// 父进程继续监控子进程
while (wait(NULL) > 0);
5. 关键注意事项
-
文件描述符处理:
-
默认保留已打开的文件描述符
-
建议对不需要的fd设置
FD_CLOEXEC标志
fcntl(fd, F_SETFD, FD_CLOEXEC);
-
-
信号处理重置:
-
被捕获的信号会恢复为默认处理方式
-
忽略的信号保持忽略状态
-
-
进程属性保留:
-
维持原PID、PPID关系
-
保持调度策略和优先级
-
6. 底层实现简图
用户态调用execve() ↓ sys_execve() (系统调用入口) ↓ do_execve() ├─ 1. 打开目标文件 ├─ 2. 解析ELF格式 ├─ 3. 检查权限 ├─ 4. 准备新的内存空间 │ ├─ flush_old_exec() // 清理原内存 │ └─ setup_new_exec() // 初始化新内存布局 └─ 5. start_thread() // 设置新的eip/esp
这种设计保证了:
-
原子性:要么完全替换成功,要么完全失败
-
安全性:新程序无法访问原进程的任何内存数据
-
高效性:复用现有进程结构,避免创建新进程的开销
相关文章:
六十天Linux从0到项目搭建(第十五天)(程序替换、exec流程示意图、核心特性)
1 为什么要有程序替换? 程序替换(Process Replacement)是操作系统中一个关键机制,它的核心目的是:让一个正在运行的进程(通常是子进程)停止执行当前代码,转而加载并执行一个全新的程…...
排序算法3-交换排序
目录 1.常见排序算法 2.排序算法的预定函数 2.1交换函数 2.2测试算法运行时间的函数 2.3已经实现过的排序算法 3.交换排序的实现 3.1冒泡排序 3.2快速排序 3.2.1递归的快速排序 3.2.1.1hoare版本的排序 3.2.1.2挖坑法 3.2.1.3lomuto前后指针法 3.2.2非递归版本的快…...
【Qt】数据库管理
数据库查询工具开发学习笔记 一、项目背景与目标 背景:频繁编写数据库查询语句,希望通过工具简化操作,提升效率。 二、总体设计思路 1. 架构设计 MVC模式:通过Qt控件实现视图(UI),业务逻辑…...
Ant Design Vue 中的table表格高度塌陷,造成行与行不齐的问题
前言: Ant Design Vue: 1.7.2 Vue2 less 问题描述: 在通过下拉框选择之后,在获取接口数据,第一列使用了fixed:left,就碰到了高度塌陷,查看元素的样式结果高度不一致,如&#x…...
面经-项目
项目 项目(重点)问题1:描述在网页中题目点击提交后到题目结果出现的一系列后台反应【1】如何获取到用户提交的代码的?【2】_1. 题目细节都有哪些?【2】_2. 题目信息怎么存储的?【3】负载均衡算法的实现?【4】oj_server怎么连接对应的compile_server(编译主机)的?【5】oj_…...
Win10安装Linux的三种方法
通过 Windows 子系统 for Linux(WSL)安装 启用 “适用于 Linux 的 Windows 子系统” 可选功能: 图形界面方式:在【设置 -> 更新与安全 -> 开发者选项】中开启【开发人员模式】;在【程序和功能 -> 启用或关闭…...
【qt】文件类(QFile)
很高兴你能看到这篇文章,同时我的语雀文档也更新了许多嵌入式系列的学习笔记希望能帮到你 : https://www.yuque.com/alive-m4b9n 目录 QFile 主要功能QFile 操作步骤QFile 其他常用函数案例分析及实现功能一实现:打开文件并显示功能二实现:另…...
力扣hot100——最长连续序列(哈希unordered_set)
题目链接:最长连续序列 1、错解:数组做哈希表(内存超出限制) int longestConsecutive(vector<int>& nums) {vector<bool> hash(20000000010, false);for(int i0; i<nums.size();i){hash[1000000000nums[i]]t…...
3. 实战(一):Spring AI Trae ,助力开发微信小程序
1、前言 前面介绍了Spring boot快速集成Spring AI实现简单的Chat聊天模式。今天立马来实战一番,通过Trae这个火爆全网的工具,来写一个微信小程序。照理说,我们只是极少量的编码应该就可以完成这项工作。开撸~ 2、需求描述 微信小程序实现一…...
MySQL高级语句深度解析与应用实践
一、窗口函数:数据分析的利器 1. 窗口函数基础概念 窗口函数(Window Function)是MySQL 8.0引入的强大特性,它可以在不减少行数的情况下对数据进行聚合计算和分析 SELECT employee_name,department,salary,RANK() OVER (PARTITION BY department ORDER…...
SSE服务器主动推送至浏览器客户端,让你不再需要websocket
Server-Sent Events(SSE)是一种服务器向客户端推送实时更新的技术,基于HTTP协议。客户端通过EventSource API来接收事件流,而服务器则保持一个长连接,持续发送数据。这与传统的请求-响应模式不同,允许服务器…...
UE5新材质系统效果Demo展示
1、玉质材质,透明玻璃材质,不同透射和散射。 2、浅水地面,地面层,水层,地面湿度,水面高度,水下扰动,水下浇洒,水下折射 Substrate-Water Substrate-Water-CodeV2...
wps 怎么显示隐藏文字
wps 怎么显示隐藏文字 》文件》选项》视图》勾选“隐藏文字” wps怎么设置隐藏文字 wps怎么设置隐藏文字...
解决 macOS (M1 Pro) 上使用 Vite 进行 Build 打包时 Node 进程内存溢出的问题
解决 macOS (M1 Pro) 上使用 Vite 进行 Build 打包时 Node 进程内存溢出的问题 在搭载 M1 Pro 芯片的 macOS 系统上,使用 Vite 进行项目构建(build)时,您可能会遇到 Node 进程内存溢出的错误。特别是在使用较新版本的 Node.js&am…...
页面重构过程中如何保证良好的跨浏览器一致性?
在页面重构的过程中,为了确保网页能够在不同的浏览器中呈现一致的效果,我们需要采取一系列措施来提高跨浏览器的一致性。以下是几个关键步骤和技术要点: 使用标准化的HTML和CSS:始终遵循最新的Web标准编写代码,例如采用…...
CXL UIO Direct P2P学习
前言: 在CXL协议中,UIO(Unordered Input/Output) 是一种支持设备间直接通信(Peer-to-Peer, P2P)的机制,旨在绕过主机CPU或内存的干预,降低延迟并提升效率。以下是UIO的核心概念及UI…...
leetcode138.随即链表的复制
思路源于 【力扣hot100】【LeetCode 138】随机链表的复制|哈希表 采用一个哈希表,键值对为<原链表的结点,新链表的结点>,第一次遍历原链表结点时只创建新链表的结点,第二次遍历原链表结点时,通过键拿…...
03_MySQL工具介绍
文章目录 一、Navicat for MySQL1.1、安装 二、SQLyog2.1、安装 多数时候使用SQL语句对数据库进行操作不是很方便,特别是在查询操作时,显示的内容不够直观,此时我们需要借助图形化工具对数据库进行操作。 操作MySQL常用的图形工具如下&#x…...
《网络管理》实践环节01:OpenEuler22.03sp4安装zabbix6.2
兰生幽谷,不为莫服而不芳; 君子行义,不为莫知而止休。 1 环境 openEuler 22.03 LTSsp4PHP 8.0Apache 2Mysql 8.0zabbix6.2.4 表1-1 Zabbix网络规划(用你们自己的特征网段规划) 主机名 IP 功能 备注 zbx6svr 19…...
Qt Creator 中文 “error: C2001: 常量中有换行符“ 问题解决方法
Qt Creator 编译时出现中文 error: C2001: 常量中有换行符的问题,通常由文件编码与编译器字符集不兼容导致。 一、修改文件编码格式 添加 UTF-8 BOM 签名 在 Qt Creator 中设置:工具 -> 选项 -> 文本编辑器 -> 行为 -> UTF-8 BOM&a…...
Charles 抓包配置保姆教程(PC、IOS、Android)
抓包工具基础配置与使用指南 大家好,我是十一!今天给大家分享一篇关于抓包工具的基础配置与使用指南。无论是开发、测试还是安全分析,抓包工具都是不可或缺的利器。本文将详细介绍如何配置和使用抓包工具,并特别推荐一款功能强大…...
洛谷题单1-P1001 A+B Problem-python-流程图重构
题目描述 输入两个整数 a,b,输出它们的和(∣a∣,∣b∣≤109)。 输入格式 两个以空格分开的整数。 输出格式 一个整数。 输入输出样例 输入 20 30输出 50方式-print class Solution:staticmethoddef oi_input():"""从…...
el-table 动态给每行增加class属性
el-table 动态给每行增加class属性 html代码 row-class-name属性,绑定方法 :row-class-name“tableRowClassName”, <el-table :data"tableData" border :row-class-name"tableRowClassName"> </el-table>js代码 tableRowClassNam…...
Opencv计算机视觉编程攻略-第四节 图直方图统计像素
Opencv计算机视觉编程攻略-第四节 图直方图统计像素 1.计算图像直方图2.基于查找表修改图像3.直方图均衡化4.直方图反向投影进行内容查找5.用均值平移法查找目标6.比较直方图搜索相似图像7.用积分图统计图像 1.计算图像直方图 图像统计直方图的概念 图像统计直方图是一种用于描…...
深度学习处理时间序列(5)
Keras中的循环层 上面的NumPy简单实现对应一个实际的Keras层—SimpleRNN层。不过,二者有一点小区别:SimpleRNN层能够像其他Keras层一样处理序列批量,而不是像NumPy示例中的那样只能处理单个序列。也就是说,它接收形状为(batch_si…...
Mysql 索引性能分析
1.查看CRUD次数 show global status like Com_______(7个下划线) show global status like Com_______ 2.慢SQL分析 SET GLOBAL slow_query_log ON;-- 设置慢SQL日志记录开启 SET GLOBAL long_query_time 2; -- 设置执行超过 2 秒的查询为慢查询 开…...
win11+ubuntu双系统安装
操作步骤: 官网下载ubuntu 最新镜像文件 准备U盘 准备一个容量不小于 8GB 的 U 盘,用于制作系统安装盘。制作过程会格式化 U 盘,请注意提前备份数据。 制作U盘启动盘 使用rufus工具,或者 balenaEtcher工具(官网安…...
linux-5.10.110内核源码分析 - 写磁盘(从VFS系统调用到I/O调度及AHCI写磁盘)
1、VFS写文件到page缓存(vfs_write) 1.1、写裸盘(dd) 使用如下命令写裸盘: dd if/dev/zero of/dev/sda bs4096 count1 seek1 1.2、系统调用(vfs_write) 系统调用栈如下: 对于调用栈的new_sync_write函数,buf为写磁盘的内容的内存地址&…...
arinc818 fpga单色图像传输ip
arinc818协议支持的常用线速率如下图 随着图像分辨率的提高,单lane的速率无法满足特定需求,一种方式是通过多个LANE交叉的去传输图像,另外一种是通过降低图像的带宽,即通过只传单色图像达到对应的效果 程序架构如下图所示&#x…...
业务流程先导及流程图回顾
一、测试流程回顾  1. 备测内容回顾  备测内容: 本次测试涵盖买家和卖家的多个业务流程,包括下单流程、发货流程、搜索退货退款、支付抢购、换货流程、个人中心优惠券等。 2. 先测业务强调  1)测试业务流程 …...
