[Linux]进程程序替换
[Linux]进程程序替换
文章目录
- [Linux]进程程序替换
- 进程程序替换的意义
- 见一见进程程序替换
- 进程程序替换的原理
- 进程程序替换中的写时拷贝
- 介绍进程程序替换接口
进程程序替换的意义
Linux系统下使用fork系统函数创建子进程后,子进程只能执行继承的部分父进程代码,如果要想让子进程执行一份单独的代码就要进行进程程序替换。
见一见进程程序替换
进程程序替换的头文件和函数如下:

编写如下代码来见一见进程程序替换:
#include <stdio.h>
#include <unistd.h>int main()
{printf("进程程序替换前\n");printf("进程程序替换前\n");printf("进程程序替换前\n");execl("/bin/ls", "ls", "-a", "-l", NULL);printf("进程程序替换后\n");printf("进程程序替换后\n");printf("进程程序替换后\n");return 0;
}
编译代码并运行查看结果:

可以看出,进程程序替换后,进程不再执行原有的代码而是转而执行替换后的代码。进程替换函数后的代码不再被执行,因此可以看出程序替换是整体替换,替换后原有的代码和数据都不存在了。
进程程序替换的原理
进程程序替换是在不修改进程pcb中的id的情况下,将磁盘中的可执行程序的代码和数据传送到内存中,替换进程原有的代码和数据,并且修改页表映射,完成进程程序的替换,示意图如下:

- 从进程的角度看:代码和数据被操作系统替换了。
- 从程序的角度看:自身的代码和数据被加载到了内存中。
程序加载的原理: 在Linux操作系统下,启动的任何一个进程都是shell进程的子进程,启动进程的就是先让shell进程创建一个子进程的pcb,然后用我们编写好的进程的代码和数据替换这个shell进程的子进程的,从而完成进程的加载启动。
进程程序替换中的写时拷贝
为了体会进程程序替换中的写时拷贝,编写如下代码:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{pid_t id = fork();if (id == 0){//子进程printf("我是子进程,我的pid:%d\n", getpid());execl("/bin/ls", "ls", "-a", "-l", NULL);}waitpid(id, NULL, 0);printf("我是父进程,我的pid:%d\n", getpid());return 0;
}
编译代码运行并且查看结果:

可以看出进程程序替换是不影响父进程的代码和数据的,因为在替换子进程的代码和数据时,发生了写时拷贝,另外可以看出实际上代码区的数据是可以修改的。
介绍进程程序替换接口
返回值
进程程序替换函数只有在执行失败时才会有返回值,如果进程程序替换失败了会返回-1,并且设置错误码。由于进程程序替换函数的替换成功后,该函数内部的返回代码也被替换了,因此一旦执行成功是不会有返回值的。一旦替换失败了,由于进程程序替换不会修改pcb,因此也不影响父进程接收子进程的退出码。
编写如下代码进行测试:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>int main()
{pid_t id = fork();if (id == 0){//子进程int n = execl("/bin/lsss", "lsss", "-a", "-l", NULL);printf("我是子进程,我的id:%d, 程序替换失败,n: %d\n", getpid(), n);exit(1);}int status = 0;waitpid(id, &status, 0);printf("我是父进程,子进程退出码:%d\n", WEXITSTATUS(status));return 0;
}
编译代码运行并查看结果:

由于我们传入的替换程序是错误的,进程程序替换失败了,接收到了返回值-1,父进程也正常接收到了退出码-1。
再编写如下代码进行测试:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>int main()
{pid_t id = fork();if (id == 0){//子进程int n = execl("/bin/ls", "ls", "hello.txt", NULL);//该文件不存在printf("我是子进程,我的id:%d, 程序替换失败,n: %d\n", getpid(), n);exit(1);}int status = 0;waitpid(id, &status, 0);printf("我是父进程,子进程退出码:%d\n", WEXITSTATUS(status));return 0;
}
编译代码运行并查看结果:

可以看出即使程序替换成功了,也不影响子进程退出码的接收。
execl函数
//execl函数声明
int execl(const char *path, const char *arg, ...);
- path参数 – 要替换的程序的路径
- arg参数 – 可变参数,接收替换程序的命令行参数,以NULL结尾
编写如下代码进行测试:
#include <stdio.h>
#include <unistd.h>int main()
{printf("进程程序替换前\n");execl("/bin/ls", "ls", "-a", "-l", NULL);printf("进程程序替换失败\n");return 0;
}
编译代码运行并查看结果:

execv函数
//execv函数声明
int execv(const char *path, char *const argv[]);
- path参数 – 要替换的程序的路径
- argv参数 – 接收要替换程序的命令行参数,以NULL结尾
编写如下代码进行测试:
#include <stdio.h>
#include <unistd.h>int main()
{printf("进程程序替换前\n");//execl("/bin/ls", "ls", "-a", "-l", NULL);char * const argv[] = {"ls","-a","-l","-n",NULL};execv("/bin/ls", argv);printf("进程程序替换失败\n");return 0;
}
编译代码运行并查看结果:

execlp函数
//execlp函数声明
int execlp(const char *file, const char *arg, ...);
- file参数 – 要替换的程序名,会自动去环境变量的路径中查找该程序
- arg参数 – 可变参数,接收替换程序的命令行参数,以NULL结尾
编写如下代码进行测试:
#include <stdio.h>
#include <unistd.h>int main()
{printf("进程程序替换前\n");execlp("ls", "ls", "-l", "-n", NULL);printf("进程程序替换失败\n");return 0;
}
编译代码运行并查看结果:

execvp函数
//execvp函数声明
int execvp(const char *file, char *const argv[]);
- file参数 – 要替换的程序名,会自动去环境变量的路径中查找该程序
- argv参数 – 接收要替换程序的命令行参数,以NULL结尾
编写如下代码进行测试:
#include <stdio.h>
#include <unistd.h>int main()
{printf("进程程序替换前\n");char * const argv[] = {"ls","-l","-n",NULL};execvp("ls", argv);printf("进程程序替换失败\n");return 0;
}
编译代码运行并查看结果:

execle函数
//execle函数声明
int execle(const char *path, const char *arg, ..., char * const envp[]);
- path参数 – 要替换的程序的路径
- arg参数 – 可变参数,接收替换程序的命令行参数,以NULL结尾
- envp参数 – 接收传给替换程序的环境变量,以NULL结尾,覆盖式传入,接收的进程中只会有传入的环境变量
编写如下文件目录结构:

使用myproc程序和otherproc程序进行测试,其代码内容如下
//myproc
#include <stdio.h>
#include <unistd.h>int main()
{printf("进程程序替换前\n");char * const envp[] = {"MYENV=YOUCANSEEME",NULL };execle("../otherproc/otherproc", "otherproc", NULL, envp);printf("进程程序替换失败\n");return 0;
}
//otherproc
#include <iostream>
#include <stdlib.h>using namespace std;int main()
{cout << " MYENV: " << (getenv("MYENV")==NULL?"NULL":getenv("MYENV")) << endl;cout << " PATH: " << (getenv("PATH")==NULL?"NULL":getenv("PATH")) << endl;return 0;
}
使用otherproc程序替换myproc程序,并且只传入只含有一个环境变量的参数envp。
编译代码运行并查看结果:

由于execle函数传入环境变量采用的是覆盖式传入,因此替换程序只有传入的一个环境变量。
补充:
- 可以使用全局变量
environ让替换程序获取父进程的全部环境变量。 - shell进程创建子进程时也是调用这样的系统接口将环境变量传入。
execvpe函数
int execvpe(const char *file, char *const argv[], char *const envp[]);
- file参数 – 要替换的程序名,会自动去环境变量的路径中查找该程序
- argv参数 – 接收要替换程序的命令行参数,以NULL结尾
- envp参数 – 接收传给替换程序的环境变量,以NULL结尾,覆盖式传入,接收的进程中只会有传入的环境变量
补充知识
程序替换函数的命名规律:
- l后缀 – 以可变参数形式接收替换程序的命令行参数
- v后缀 – 以指针数组的形式接收替换程序的命令行参数
- p后缀 – 传入替换程序的程序名,会自动在环境变量的路径中查找
- e后缀 – 以指针数组的形式接收替换程序的环境变量
以上所有的进程程序替换系统调用都是对execve系统调用函数的封装,不同的封装是为了更适合不同的应用场景。
相关文章:
[Linux]进程程序替换
[Linux]进程程序替换 文章目录 [Linux]进程程序替换进程程序替换的意义见一见进程程序替换进程程序替换的原理进程程序替换中的写时拷贝介绍进程程序替换接口 进程程序替换的意义 Linux系统下使用fork系统函数创建子进程后,子进程只能执行继承的部分父进程代码&…...
读余华小说《兄弟》
上部读完的一些笔记和思考,下部 TODO 时间:上世纪6、70年代 地点:刘镇 人物:故事中的兄弟指的是:宋钢(兄),李光头(弟),如下为简单的人物和命运图 一些故事:…...
机器学习课后习题 --回归
(一)单选题 1.以下()组变量之间存在线性回归关系? A:学生的性别与他的成绩 B:儿子的身高与父亲的身高 C:正方形的边长与面积D: 正三角形的边长与周长 2.回归问题和分类问题的区别是? A:回归问题有标签,分类问题…...
【golang】15、cobra cli 命令行库
Cobra 是 golang 最流行的命令行库,文档见 一、脚手架 mkdir pt && cd pt && go mod init cobra-cli init # 在项目下运行即可生成脚手架# tree . ├── LICENSE ├── cmd # 生成了cmd目录 │ └── root.go # 生成了root.go, 其中定义了ro…...
黑马 大事件项目 笔记
学习视频:黑马 Vue23 课程 后台数据管理系统 - 项目架构设计 在线演示:https://fe-bigevent-web.itheima.net/login 接口文档: https://apifox.com/apidoc/shared-26c67aee-0233-4d23-aab7-08448fdf95ff/api-93850835 接口根路径: http:/…...
C#2010 .NET4 解析 json 字符串
下载Newtonsoft.Json.dll using System; using System.Collections.Generic; using System.Linq; using System.Text;using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; namespace ConsoleApplication1 {class Program{static void Main(string[] args){strin…...
flutter Could not get unknown property ‘ndkVersion’
使用的 flutter 版本为 3.7.2 ,编译运行 如下 Could not get unknown property ‘ndkVersion’ for object of type com.android.build.gradle.internal.dsl.BaseAppModuleExtension 解决方法是 在flutter-3.7.2\packages\flutter_tools\gradle\flutter.gradle配置…...
WebSocket--技术文档--架构体系--《WebSocket实现原理以及关键组件》
WebSocket产生背景 简单的说,WebSocket协议之前,双工通信是通过多个http链接来实现,这导致了效率低下。WebSocket解决了这个问题。下面是标准RFC6455中的产生背景概述。 长久以来, 创建实现客户端和用户端之间双工通讯的web app都会造成HTT…...
LeetCode-45-跳跃游戏Ⅱ-贪心算法
题目描述: 给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。 每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i j] 处: 0 < j < nums[i] i j < n 返回到…...
商品详情接口使用 API 调用获取商品数据的完整方案
在电子商务应用程序中,商品详情接口是不可或缺的一部分。它用于从电商平台或自己的数据库中获取商品数据,并将其提供给应用程序的其他部分使用。本文将详细介绍如何设计一个完整的商品详情接口方案,其中包括使用 API 调用来获取商品数据的过程…...
vue+element-ui el-table组件二次封装实现虚拟滚动,解决数据量大渲染DOM过多而卡顿问题
一、此功能已集成到TTable组件中 二、最终效果 三、需求 某些页面不做分页时,当数据过多,会导致页面卡顿,甚至卡死 四、虚拟滚动 一、固定一个可视区域的大小并且其大小是不变的,那么要做到性能最大化就需要尽量少地渲染 DOM 元素…...
5.1 树和二叉树的定义
思维导图: 问题 为什么有树和二叉树? "树" 和 "二叉树" 都是数据结构中常用的结构,它们分别有其独特的应用和优点。我们可以从它们的定义和特性中理解为什么它们都存在。 1. **树 (Tree):** - **定义**:…...
Java单元测试及常用语句 | 京东物流技术团队
1 前言 编写Java单元测试用例,即把一段复杂的代码拆解成一系列简单的单元测试用例,并且无需启动服务,在短时间内测试代码中的处理逻辑。写好Java单元测试用例,其实就是把“复杂问题简单化,建单问题深入化“。在编写的…...
详解Vue中的render: h => h(App)
声明:只是记录,会有错误,谨慎阅读 我们用脚手架初始化工程的时候,main.js的代码如下 import Vue from vue import App from ./App.vueVue.config.productionTip falsenew Vue({// 把app组件放入容器中render: h > h(App), }).$mount(#ap…...
归并排序的详解!
本文旨在讲解归并排序的实现(递归及非递归)搬好小板凳,干货来了! 前序: 在介绍归并排序之前,需要给大家介绍的是什么是归并,归并操作,也叫归并算法,指的是将两个顺序序列…...
排盘程序算法探寻举例(陆先生八字)
算法实现: 1.庚生未月,燥土不能生金,日支申金为日主墙根,月干辛金比劫透出傍身,月干强。年干甲木自做寅木强根,又得月支乙木中气,甲木强旺有力,时干丙火七杀得未土余气,…...
考研408 | 【操作系统】终章
I/O设备的基本概念和分类 I/O设备: I/O设备的分类 1.按使用特性: 2.按传输速率分类: 3.按信息交换的单位分类: 总结: I/O控制器 I/O设备的机械部件: I/O设备的电子部件(I/O控制器&#…...
亚马逊云科技生成式AI技术辅助教学领域,近实时智能应答2D数字人搭建
早在大语言模型如GPT-3.5等的兴起和被日渐广泛的采用之前,教育行业已经在AI辅助教学领域有过各种各样的尝试。在教育行业,人工智能技术的采用帮助教育行业更好地实现教学目标,提高教学质量、学习效率、学习体验、学习成果。例如,人…...
Programming abstractions in C阅读笔记:p139-p143
《Programming Abstractions In C》学习第55天,p139-p140,总结如下: 一、技术总结 1.文件I/O操作 文件I/O操作可以分为一下这些步骤: (1)声明文件指针对象。 File *infile;(2)打开文件 fopen()。打开文件的模式有“r”, “w…...
MyBatis-Plus学习笔记
1.MyBatis-Plus简介: MyBatis-Plus是一个MyBatis的增强工具,在MyBatis的基础上只做增强不做改变,为简化开发、提高效率而生。MyBatis-Plus提供了通用的mapper和service,可以在不编写任何SQL语句的情况下,快速的实现对单…...
C++实现分布式网络通信框架RPC(3)--rpc调用端
目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中,我们已经大致实现了rpc服务端的各项功能代…...
MySQL 8.0 OCP 英文题库解析(十三)
Oracle 为庆祝 MySQL 30 周年,截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始,将英文题库免费公布出来,并进行解析,帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...
Android第十三次面试总结(四大 组件基础)
Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机: onCreate() 调用时机:Activity 首次创建时调用。…...
群晖NAS如何在虚拟机创建飞牛NAS
套件中心下载安装Virtual Machine Manager 创建虚拟机 配置虚拟机 飞牛官网下载 https://iso.liveupdate.fnnas.com/x86_64/trim/fnos-0.9.2-863.iso 群晖NAS如何在虚拟机创建飞牛NAS - 个人信息分享...
DeepSeek源码深度解析 × 华为仓颉语言编程精粹——从MoE架构到全场景开发生态
前言 在人工智能技术飞速发展的今天,深度学习与大模型技术已成为推动行业变革的核心驱动力,而高效、灵活的开发工具与编程语言则为技术创新提供了重要支撑。本书以两大前沿技术领域为核心,系统性地呈现了两部深度技术著作的精华:…...
VisualXML全新升级 | 新增数据库编辑功能
VisualXML是一个功能强大的网络总线设计工具,专注于简化汽车电子系统中复杂的网络数据设计操作。它支持多种主流总线网络格式的数据编辑(如DBC、LDF、ARXML、HEX等),并能够基于Excel表格的方式生成和转换多种数据库文件。由此&…...
【HarmonyOS 5】鸿蒙中Stage模型与FA模型详解
一、前言 在HarmonyOS 5的应用开发模型中,featureAbility是旧版FA模型(Feature Ability)的用法,Stage模型已采用全新的应用架构,推荐使用组件化的上下文获取方式,而非依赖featureAbility。 FA大概是API7之…...
C++中vector类型的介绍和使用
文章目录 一、vector 类型的简介1.1 基本介绍1.2 常见用法示例1.3 常见成员函数简表 二、vector 数据的插入2.1 push_back() —— 在尾部插入一个元素2.2 emplace_back() —— 在尾部“就地”构造对象2.3 insert() —— 在任意位置插入一个或多个元素2.4 emplace() —— 在任意…...
GB/T 43887-2024 核级柔性石墨板材检测
核级柔性石墨板材是指以可膨胀石墨为原料、未经改性和增强、用于核工业的核级柔性石墨板材。 GB/T 43887-2024核级柔性石墨板材检测检测指标: 测试项目 测试标准 外观 GB/T 43887 尺寸偏差 GB/T 43887 化学成分 GB/T 43887 密度偏差 GB/T 43887 拉伸强度…...
java 局域网 rtsp 取流 WebSocket 推送到前端显示 低延迟
众所周知 摄像头取流推流显示前端延迟大 传统方法是服务器取摄像头的rtsp流 然后客户端连服务器 中转多了,延迟一定不小。 假设相机没有专网 公网 1相机自带推流 直接推送到云服务器 然后客户端拉去 2相机只有rtsp ,边缘服务器拉流推送到云服务器 …...
