六十天Linux从0到项目搭建(第十五天)(程序替换、exec流程示意图、核心特性)

news2025/4/2 15:45:40

为什么要有程序替换?

程序替换(Process Replacement)是操作系统中一个关键机制,它的核心目的是:让一个正在运行的进程(通常是子进程)停止执行当前代码,转而加载并执行一个全新的程序

核心需求
  1. 子进程需要执行不同的任务

    • 父进程创建子进程后,子进程默认会执行父进程的代码(fork() 后的代码)。

    • 但如果子进程需要执行 完全不同的程序(如 lsgcc 等),就需要程序替换。

  2. 避免重复创建进程的开销

    • 如果每次运行新程序都先 fork() 再 exec(),可以复用已有的进程结构(如 PID、文件描述符等),比直接创建新进程更高效。

  3. 实现灵活的进程管理

    • 比如 Shell 执行命令时:

      $ ls -l  # Shell fork() + exec("ls") 来运行 `ls`,而不是自己实现 `ls` 的功能

2. 创建子进程的目的是什么?

(1)让子进程执行父进程的一部分代码
  • 默认情况下,fork() 创建的子进程会 继承父进程的代码和数据,继续执行 fork() 之后的逻辑。

  • 适用场景

    • 多进程并行计算(如分治算法)。

    • 父进程和子进程协作完成同一任务(如网络服务器处理请求)。

(2)让子进程执行一个全新的程序
  • 通过 程序替换(exec 系列函数),子进程可以加载另一个程序的代码和数据,完全替换自己。

  • 适用场景

    • Shell 运行外部命令(如 lsgrep)。

    • 动态加载插件或子模块(如游戏引擎加载新关卡逻辑)。


3. 程序替换的本质

程序替换通过 exec 系列函数 实现,它的核心行为是:

  1. 替换当前进程的代码和数据

    • 销毁当前进程的 代码段、数据段、堆栈,重新加载目标程序的代码和数据。

    • 注意PID、文件描述符、信号处理等 进程属性不变

  2. 不创建新进程

    • exec 只是 替换当前进程的内容,不会像 fork() 那样新建进程。

  3. 成功后永不返回

    • 如果 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 像 灵魂转移,保留身体(进程资源),但彻底换了一个大脑(程序代码)

程序替换(Process Replacement)的原理

程序替换是操作系统提供的一种机制,允许一个进程 动态加载并执行另一个全新的程序,而无需创建新的进程。它的核心原理可以分为以下几个部分:


1. 程序替换的核心函数

在 Linux 中,程序替换通过 exec 系列函数 实现,主要包括:

  • execlexeclpexecle(参数列表形式)

  • execvexecvpexecvpe(参数数组形式)

示例

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):仍然是原来的进程,只是内容变了。

  • 文件描述符表:已打开的文件(如 stdinstdout)仍然有效(除非显式设置 O_CLOEXEC)。

  • 进程优先级、信号处理方式(除非新程序重新设置)。

  • 用户 ID、组 ID(除非新程序是 setuid/setgid 程序)。

(3)不创建新进程

  • exec 不会创建新进程,只是替换当前进程的代码和数据。

  • 如果 exec 成功,原进程的代码不再执行,控制权交给新程序。

  • 如果 exec 失败(如目标程序不存在),返回 -1,并继续执行原程序。


3. 程序替换的底层机制

(1)内核的处理流程

  1. 解析目标程序

    • 检查目标程序是否存在,是否有执行权限。

    • 读取目标程序的 ELF 头部信息(如代码段、数据段的布局)。

  2. 释放原进程资源

    • 销毁原进程的代码、数据、堆、栈等内存区域。

    • 关闭标记了 O_CLOEXEC 的文件描述符。

  3. 加载新程序

    • 将新程序的代码段(.text)、数据段(.data)加载到内存。

    • 设置新的堆栈(stack)和堆(heap)。

  4. 更新进程的页表

    • 建立虚拟地址到物理地址的新映射。

  5. 跳转到新程序的入口点(通常是 _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 像 换脑手术(保留身体,换新大脑)。

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)
↓
--------------------------------------------------------------
用户态
↓
新程序开始执行(原进程的代码已完全消失)

关键点说明

  1. 权限检查阶段

    • 确认目标文件存在且可执行

    • 检查 setuid/setgid 权限位

  2. ELF 加载阶段

    • 通过 mmap 建立代码段(只读)和数据段(可写)的映射

    • 初始化 .bss 段为零页

  3. 资源清理阶段

    • 关闭标记了 FD_CLOEXEC 的文件描述符

    • 保留标准输入/输出/错误流

  4. 执行转移阶段

    • 通过修改 rip 寄存器跳转到新程序的 _start 符号

    • 完全不会回到原调用点(除非加载失败)

典型错误路径

execve("/not/exist", ...)
↓
内核发现文件不存在
↓
返回 -1 (errno=ENOENT)
↓
原进程继续执行(检查返回值)

与 fork() 的对比

fork() 流程:
用户进程 → 复制PCB → 复制页表 → 返回两次

exec() 流程:
用户进程 → 销毁内存映射 → 新建映射 → 永不返回(成功时)

这个流程体现了 Linux "进程是执行流的容器" 的设计哲学:保持进程外壳(PID、文件描述符等),仅替换内部执行内容。

多进程环境下程序替换(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. 关键注意事项
  1. 文件描述符处理

    • 默认保留已打开的文件描述符

    • 建议对不需要的fd设置FD_CLOEXEC标志

    fcntl(fd, F_SETFD, FD_CLOEXEC);
  2. 信号处理重置

    • 被捕获的信号会恢复为默认处理方式

    • 忽略的信号保持忽略状态

  3. 进程属性保留

    • 维持原PID、PPID关系

    • 保持调度策略和优先级

6. 底层实现简图
用户态调用execve()
↓
sys_execve() (系统调用入口)
↓
do_execve()
├─ 1. 打开目标文件
├─ 2. 解析ELF格式
├─ 3. 检查权限
├─ 4. 准备新的内存空间
│   ├─ flush_old_exec()  // 清理原内存
│   └─ setup_new_exec()  // 初始化新内存布局
└─ 5. start_thread()    // 设置新的eip/esp

这种设计保证了:

  • 原子性:要么完全替换成功,要么完全失败

  • 安全性:新程序无法访问原进程的任何内存数据

  • 高效性:复用现有进程结构,避免创建新进程的开销

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2325673.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

排序算法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】数据库管理

数据库查询工具开发学习笔记 一、项目背景与目标 背景&#xff1a;频繁编写数据库查询语句&#xff0c;希望通过工具简化操作&#xff0c;提升效率。 二、总体设计思路 1. 架构设计 MVC模式&#xff1a;通过Qt控件实现视图&#xff08;UI&#xff09;&#xff0c;业务逻辑…

Ant Design Vue 中的table表格高度塌陷,造成行与行不齐的问题

前言&#xff1a; Ant Design Vue: 1.7.2 Vue2 less 问题描述&#xff1a; 在通过下拉框选择之后&#xff0c;在获取接口数据&#xff0c;第一列使用了fixed:left&#xff0c;就碰到了高度塌陷&#xff0c;查看元素的样式结果高度不一致&#xff0c;如&#x…

【qt】文件类(QFile)

很高兴你能看到这篇文章&#xff0c;同时我的语雀文档也更新了许多嵌入式系列的学习笔记希望能帮到你 &#xff1a; https://www.yuque.com/alive-m4b9n 目录 QFile 主要功能QFile 操作步骤QFile 其他常用函数案例分析及实现功能一实现&#xff1a;打开文件并显示功能二实现:另…

3. 实战(一):Spring AI Trae ,助力开发微信小程序

1、前言 前面介绍了Spring boot快速集成Spring AI实现简单的Chat聊天模式。今天立马来实战一番&#xff0c;通过Trae这个火爆全网的工具&#xff0c;来写一个微信小程序。照理说&#xff0c;我们只是极少量的编码应该就可以完成这项工作。开撸~ 2、需求描述 微信小程序实现一…

UE5新材质系统效果Demo展示

1、玉质材质&#xff0c;透明玻璃材质&#xff0c;不同透射和散射。 2、浅水地面&#xff0c;地面层&#xff0c;水层&#xff0c;地面湿度&#xff0c;水面高度&#xff0c;水下扰动&#xff0c;水下浇洒&#xff0c;水下折射 Substrate-Water Substrate-Water-CodeV2

wps 怎么显示隐藏文字

wps 怎么显示隐藏文字 》文件》选项》视图》勾选“隐藏文字” wps怎么设置隐藏文字 wps怎么设置隐藏文字

CXL UIO Direct P2P学习

前言&#xff1a; 在CXL协议中&#xff0c;UIO&#xff08;Unordered Input/Output&#xff09; 是一种支持设备间直接通信&#xff08;Peer-to-Peer, P2P&#xff09;的机制&#xff0c;旨在绕过主机CPU或内存的干预&#xff0c;降低延迟并提升效率。以下是UIO的核心概念及UI…

leetcode138.随即链表的复制

思路源于 【力扣hot100】【LeetCode 138】随机链表的复制&#xff5c;哈希表 采用一个哈希表&#xff0c;键值对为<原链表的结点&#xff0c;新链表的结点>&#xff0c;第一次遍历原链表结点时只创建新链表的结点&#xff0c;第二次遍历原链表结点时&#xff0c;通过键拿…

《网络管理》实践环节01:OpenEuler22.03sp4安装zabbix6.2

兰生幽谷&#xff0c;不为莫服而不芳&#xff1b; 君子行义&#xff0c;不为莫知而止休。 1 环境 openEuler 22.03 LTSsp4PHP 8.0Apache 2Mysql 8.0zabbix6.2.4 表1-1 Zabbix网络规划&#xff08;用你们自己的特征网段规划&#xff09; 主机名 IP 功能 备注 zbx6svr 19…

Opencv计算机视觉编程攻略-第四节 图直方图统计像素

Opencv计算机视觉编程攻略-第四节 图直方图统计像素 1.计算图像直方图2.基于查找表修改图像3.直方图均衡化4.直方图反向投影进行内容查找5.用均值平移法查找目标6.比较直方图搜索相似图像7.用积分图统计图像 1.计算图像直方图 图像统计直方图的概念 图像统计直方图是一种用于描…

深度学习处理时间序列(5)

Keras中的循环层 上面的NumPy简单实现对应一个实际的Keras层—SimpleRNN层。不过&#xff0c;二者有一点小区别&#xff1a;SimpleRNN层能够像其他Keras层一样处理序列批量&#xff0c;而不是像NumPy示例中的那样只能处理单个序列。也就是说&#xff0c;它接收形状为(batch_si…

Mysql 索引性能分析

1.查看CRUD次数 show global status like Com_______&#xff08;7个下划线&#xff09; show global status like Com_______ 2.慢SQL分析 SET GLOBAL slow_query_log ON;-- 设置慢SQL日志记录开启 SET GLOBAL long_query_time 2; -- 设置执行超过 2 秒的查询为慢查询 开…

win11+ubuntu双系统安装

操作步骤&#xff1a; 官网下载ubuntu 最新镜像文件 准备U盘 准备一个容量不小于 8GB 的 U 盘&#xff0c;用于制作系统安装盘。制作过程会格式化 U 盘&#xff0c;请注意提前备份数据。 制作U盘启动盘 使用rufus工具&#xff0c;或者 balenaEtcher工具&#xff08;官网安…

linux-5.10.110内核源码分析 - 写磁盘(从VFS系统调用到I/O调度及AHCI写磁盘)

1、VFS写文件到page缓存(vfs_write) 1.1、写裸盘(dd) 使用如下命令写裸盘&#xff1a; dd if/dev/zero of/dev/sda bs4096 count1 seek1 1.2、系统调用(vfs_write) 系统调用栈如下&#xff1a; 对于调用栈的new_sync_write函数&#xff0c;buf为写磁盘的内容的内存地址&…

arinc818 fpga单色图像传输ip

arinc818协议支持的常用线速率如下图 随着图像分辨率的提高&#xff0c;单lane的速率无法满足特定需求&#xff0c;一种方式是通过多个LANE交叉的去传输图像&#xff0c;另外一种是通过降低图像的带宽&#xff0c;即通过只传单色图像达到对应的效果 程序架构如下图所示&#x…

业务流程先导及流程图回顾

一、测试流程回顾 &#xfeff; 1. 备测内容回顾 &#xfeff; 备测内容: 本次测试涵盖买家和卖家的多个业务流程&#xff0c;包括下单流程、发货流程、搜索退货退款、支付抢购、换货流程、个人中心优惠券等。 2. 先测业务强调 &#xfeff; 1&#xff09;测试业务流程 …

HCIP(RSTP+MSTP)

一、STP的重新收敛&#xff1a; 复习STP接口状态 STP初次收敛至少需要50秒的时间。STP的重新收敛情况&#xff1a; 检测到拓扑变化&#xff1a;当网络中的链路故障或新链路加入时&#xff0c;交换机会检测到拓扑变化。 选举新的根桥&#xff1a;如果原来的根桥故障或与根桥直…

《无线江湖五绝:BLE/WiFi/ZigBee的频谱大战》

点击下面图片带您领略全新的嵌入式学习路线 &#x1f525;爆款热榜 88万阅读 1.6万收藏 文章目录 **第一回武林大会&#xff0c;群雄并起****第二回WiFi的“降龙十八掌”****第三回BLE的“峨眉轻功”****第四回ZigBee的“暗器百解”****第五回LoRa的“千里传音”****第六回NB…

QT第六课------QT界面优化------QSS

作者前言 &#x1f382; ✨✨✨✨✨✨&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f382; ​&#x1f382; 作者介绍&#xff1a; &#x1f382;&#x1f382; &#x1f382; &#x1f389;&#x1f389;&#x1f389…