进程程序替换
✅<1>主页::我的代码爱吃辣
📃<2>知识讲解:Linux——进程替换
☂️<3>开发环境:Centos7
💬<4>前言:我们创建子进程的目的是什么?想让子进程帮我们执行特定的任务。那么如何让子进程去执行一段新的代码呢?
一.背景
二.子进程程序替换
三.替换函数
1.execv
2.execlp
3.execle
4.命名理解
四.实现minishell

一.背景
我们创建子进程的目的是什么?想让子进程帮我们执行特定的任务。
1.让子进程执行父进程的一部分代码
2.如果子进程指向一个全新的代码呢?这就是进程的程序替换。
见一见单进程版本进程替换,即父进程指向一个全新的代码:
隆重介绍一个接口:
int execl(const char *path, const char *arg, ...);
- path:替换程序的路径。
- arg:如何执行该程序。
- 可变参数:如何执行该程序的参数等。
测试代码:
#include <stdio.h>
#include <unistd.h>int main()
{printf("--------------------begin-------------\n");execl("/usr/bin/ls", "ls", "-a", "-l", NULL);printf("--------------------end---------------\n");return 0;
}
注意:
- 我们想替换的程序 "ls -a -l"。
- ls 的路径:/usr/bin/ls。
- "-a" "-l" 时ls命令的参数。
- 最后结束要以NULL结尾。
测试结果:

注意:
- 进程替换以后我们,并没有看到我们源代码里面的最后一个打印。
- 原因是程序在替换以后,在后续执行完ls的代码就会退出了,不会回到execl调用后面。
二.子进程程序替换
创建好子进程,让子进程调用execl,让子进程去执行替换的程序。
用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。
测试代码:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{int status;printf("--------------------begin-------------\n");pid_t pid = fork();if (pid == 0){// 我们想替换的程序 "ls -a -l"// ls 的路径:/usr/bin/ls//"-a" "-l" 时ls命令的参数// 最后结束要以NULL结尾execl("/usr/bin/ls", "ls", "-a", "-l", NULL);}waitpid(-1, &status, 0);//阻塞等待子进程退出。if (WIFEXITED(status))printf("子进程退出,退出码:%d\n", WEXITSTATUS(status));elseprintf("子进程异常,收到信号%d\n", status & 0x7F);printf("--------------------end---------------\n");return 0;
}
测试结果:

进程替换原理:
当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动
例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。

三.替换函数
其实有六种以exec开头的函数,统称exec函数:
#include <unistd.h>int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ...,char *const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *path, char *const argv[], char *const envp[]);
函数解释:
- 这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。
- 如果调用出错则返回-1。
- 所以exec函数只有出错的返回值而没有成功的返回值。
介绍其中几个:
1.execv
int execv(const char *path, char *const argv[]);
- path:程序所在的路径,
- argv:是一个指针数组,数组每一个元素代表我们需要怎么执行程序。
测试代码:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{int status;printf("--------------------begin-------------\n");pid_t pid = fork();if (pid == 0){// 我们想替换的程序 "ls -a -l"// ls 的路径:/usr/bin/ls//"-a" "-l" 时ls命令的参数// 最后结束要以NULL结尾char *argv[] = {"ls", "-a", "-l", NULL};execv("/usr/bin/ls", argv);}waitpid(-1, &status, 0);if (WIFEXITED(status))printf("子进程退出,退出码:%d\n", WEXITSTATUS(status));elseprintf("子进程异常,收到信号%d\n", status & 0x7F);printf("--------------------end---------------\n");return 0;
}
测试结果:

2.execlp
int execlp(const char *file, const char *arg, ...);
- file:程序名称。
- 后续参数:需要怎么执行程序。
- 不需要给出程序路径,execlp会自己到环境变量中找对应的程序。
测试代码:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{int status;printf("--------------------begin-------------\n");pid_t pid = fork();if (pid == 0){// 我们想替换的程序 "ls -a -l"// ls 的路径:/usr/bin/ls//"-a" "-l" 时ls命令的参数// 最后结束要以NULL结尾execlp("ls", "ls", "-a", "-l", NULL);}waitpid(-1, &status, 0);if (WIFEXITED(status))printf("子进程退出,退出码:%d\n", WEXITSTATUS(status));elseprintf("子进程异常,收到信号%d\n", status & 0x7F);printf("--------------------end---------------\n");return 0;
}
测试结果:

3.execle
int execle(const char *path, const char *arg, ...,char *const envp[]);
- path:程序路径。
- envp:是一个指针数组,用来传输环境变量。
- arg:我们如何执行程序。
测试代码:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{extern char **environ;int status;printf("--------------------begin-------------\n");pid_t pid = fork();if (pid == 0){// 我们想替换的程序 "ls -a -l"// ls 的路径:/usr/bin/ls//"-a" "-l" 时ls命令的参数// 最后结束要以NULL结尾execle("./newdir/otherproc", "otherproc", NULL, environ);}waitpid(-1, &status, 0);if (WIFEXITED(status))printf("子进程退出,退出码:%d\n", WEXITSTATUS(status));elseprintf("子进程异常,收到信号%d\n", status & 0x7F);printf("--------------------end---------------\n");return 0;
}
newdir/otherproc.cc
#include <iostream>
#include <unistd.h>
#include <stdlib.h>using namespace std;int main()
{cout << "hello world"<< "hello:" << getenv("hello") << endl;cout << "hello world"<< "hello:" << getenv("hello") << endl;cout << "hello world"<< "hello:" << getenv("hello") << endl;return 0;
}
测试结果:

4.命名理解
这些函数原型看起来很容易混,但只要掌握了规律就很好记。
- l(list) : 表示参数采用列表
- v(vector) : 参数用数组
- p(path) : 有p自动搜索环境变量PATH
- e(env) : 表示自己维护环境变量

exec调用总结:
#include <unistd.h>
int main()
{char *const argv[] = {"ps", "-ef", NULL};char *const envp[] = {"PATH=/bin:/usr/bin", "TERM=console", NULL};execl("/bin/ps", "ps", "-ef", NULL);// 带p的,可以使用环境变量PATH,无需写全路径execlp("ps", "ps", "-ef", NULL);// 带e的,需要自己组装环境变量execle("ps", "ps", "-ef", NULL, envp);execv("/bin/ps", argv);// 带p的,可以使用环境变量PATH,无需写全路径execvp("ps", argv);// 带e的,需要自己组装环境变量execve("/bin/ps", argv, envp);exit(0);
}
事实上,只有execve是真正的系统调用,其它五个函数最终都调用 execve,所以execve在man手册 第2节,其它函数在man手册第3节。这些函数之间的关系如下图所示。
四.实现minishell
用下图的时间轴来表示事件的发生次序。其中时间从左向右。shell由标识为sh的方块代表,它随着时间的流逝从左向右移动。shell从用户读入字符串"ls"。shell建立一个新的进程,然后在那个进程中运行ls程序并等待那个进程结束。

然后shell读取新的一行输入,建立一个新的进程,在这个进程中运行程序 并等待这个进程结束。
所以要写一个shell,需要循环以下过程:
1. 获取命令行
2. 解析命令行
3. 建立一个子进程(fork)
4. 替换子进程(execvp
代码:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <assert.h>#define SEP " "
#define MAX 1024
#define MAX_SUB 64// 分割命令"ls -a -l" --> "ls","-a" "-l"
void split(char buff[], char *subbuff[])
{assert(buff);assert(subbuff);int i = 0;subbuff[i++] = strtok(buff, SEP);while (subbuff[i++] = strtok(NULL, SEP)) // 如果没有找到分割,就会返回NULL;
}// 代码测定,打印命令
void debug(char *subbuff[])
{int i = 0;while (subbuff[i]){printf("%s\n", subbuff[i++]);}
}// 显示环境变量
void showenv()
{extern char **environ;for (int i = 0; environ[i]; i++){printf("[%d]:%s\n", i, environ[i]);}
}int main()
{int status = 0; // 退出码参数char myenv[32][512] = {0}; // 用户自定义环境变量int index_env = 0;int last_exit = 0;while (1){char buff[MAX] = {0}; // 存储命令行输入的命令字符串char *subbuff[MAX_SUB] = {NULL};printf("wq@[aliyum]$:");fflush(stdout);// 1.获得命令字符串fgets(buff, sizeof(buff), stdin);// 2.解析命令// ls -a -l\n\0,strlen=9,index(\n)=8,去除回车。buff[strlen(buff) - 1] = '\0';// 分割字符串split(buff, subbuff);// 处理内建命令// 注意:cd,export,env,echo,等命令都是内建命令,即这些命令不能创建子进程执行,只能bash自己执行if (strcmp(subbuff[0], "cd") == 0){if (subbuff[1] != NULL)chdir(subbuff[1]);continue;}else if (strcmp(subbuff[0], "export") == 0){if (subbuff[1] != NULL){// 这里不能将subbuff[1]直接导入环境变量,因为环境变量表存储的都是指针,必须使用一个单独空间strcpy(myenv[index_env], subbuff[1]);putenv(myenv[index_env++]);}continue;}else if (strcmp(subbuff[0], "env") == 0){showenv();continue;}else if (strcmp(subbuff[0], "echo") == 0){// echo $PATHif (subbuff[1][0] == '$'){if (subbuff[1][1] == '?') // echo $?//最近一次推出吗{printf("%d\n", last_exit);}else // 提取环境变量{// PATHchar *subenv = subbuff[1] + 1;char *get_env = getenv(subenv);if (get_env != NULL){printf("%s=%s\n", subenv, get_env);}}}continue;}if (strcmp(subbuff[0], "ls") == 0){int comm_index = 0;while (subbuff[comm_index]){comm_index++;}// 增加高亮subbuff[comm_index] = "--color=auto";}// 3.创建子进程pid_t pid = fork();assert(pid >= 0);if (pid == 0) // 子进程{extern char **environ;// 4. 程序替换execve(subbuff[0], subbuff, environ);}// // 测试// debug(subbuff);waitpid(pid, &status, 0);if (WIFEXITED(status)){// 子进程退出设置退出码last_exit = WEXITSTATUS(status);}}return 0;
}
相关文章:
进程程序替换
✅<1>主页::我的代码爱吃辣 📃<2>知识讲解:Linux——进程替换 ☂️<3>开发环境:Centos7 💬<4>前言:我们创建子进程的目的是什么?想让子进程帮我们执行特定的…...
理解HTTPS/TLS/SSL(二)可视化TLS握手过程并解密加密数据
文章目录 WireShark抓包TLS握手过程Client HelloServer HelloEncryped Extenstions, Certificate, Certificate VerifyChange Ciper Spec, FinshedTLS 1.2和TLS 1.3的区别能不能在进一步? 解密WireShark中抓到的TLS包参考资料 上一篇文章已经在本地使用了生成自签名…...
一文详解TCP三次握手四次挥手
文章目录 TCP的三次握手和四次挥手三次握手四次挥手 TCP的三次握手和四次挥手 基本概念 SYN(Synchronize Sequence Numbers,同步序列数字):用于建立连接的同步信号。 SYN 序列号的作用是用于标识每个数据包中的字节流的起始位置。…...
PDF怎么转图片?四种转换方法分享
PDF文件是一种非常常见的文档格式,然而,有时候我们需要将PDF文件转换成图片格式。比如我们可能需要将PDF文件中的某些页面或图表转换成图片格式以便于编辑或分享。在这篇文章中,我们将介绍四种将PDF文件转换成图片的方法。 方法一:…...
华为OD机试 - 压缩报文还原 - 正则表达式(Java 2023 B卷 100分)
目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路五、Java算法源码六、效果展示1、输入2、输出3、说明 华为OD机试 2023B卷题库疯狂收录中,刷题点这里 专栏导读 本专栏收录于《华为OD机试(JAVA)真题(A卷B卷&#…...
电商API的应用价值:淘宝1688京东API接口系列
API接口是一种软件应用程序,它充当两个不同软件应用程序之间的中介。它帮助不同的应用程序相互通信,共享数据,从而使用户能够完成不同的任务。API接口的用途非常广泛,下面是一些常见的用途: 数据共享:API接…...
day38 代码回想录 斐波那契数爬楼梯使用最小花费爬楼梯
大纲 ● 理论基础 ● 509. 斐波那契数 ● 70. 爬楼梯 ● 746. 使用最小花费爬楼梯 509. 斐波那契数 题目:509. 斐波那契数 // 斐波那契数列 // 动规 5部曲 // 1 dp[i]代表i处的斐波那契值 // 2 递归公式:dp[0] 0, dp[1]1, dp[i]dp[i-1]dp[i-2] // 3…...
Flink DataStream 体系
前言 本文隶属于专栏《大数据技术体系》,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢! 本专栏目录结构和参考文献请见大数据技术体系 思维导图 正文 对 Flink 这种以流为核心的分布式计…...
Linux的调试工具 - gdb(超详细)
Linux的调试工具 - gdb 1. 背景2. 开始使用指令的使用都用下面这个C语言简单小代码来进行演示:1. list或l 行号:显示文件源代码,接着上次的位置往下列,每次列10行。2. list或l 函数名:列出某个函数的源代码。3. r或run: 运行程序。…...
已知平面内三点,求其平面的法向量
三点平面法向量 设三点坐标为A(x1,y1,z1),B(x2,y2,z2),C(x3,y3,z3) 向量AB(x2-x1,y2-y1,z2-z1),AC(x3-x1,y3-y1,z3-z1) AB、AC所在平面的法向量即ABAC(a,b,c),其中: a(y2-y1)(z3-z1)-(z2-z1)(y3-y1) b(z2-z1)(x3-x1)-(z3-z1)(x2-x1) c(x2-x1)(y3-y1)-(x3-x1)(y2-y1)…...
HTML
HTML 1.HTML结构 1.1认识HTML HTML是超文本标记语言,电脑上看到的所有网站都是html实现的 HTML代码是“标签”构成的,简单来说,html就是一堆标签的组合 形如 <body>hello</body>标签名 (body) 放到 < > 中 大部分标签成…...
Java手写最大子数组和算法(如Kadane算法)和最大子数组和算法(如Kadane算法)应用拓展案例
Java手写最大子数组和算法(如Kadane算法)和最大子数组和算法(如Kadane算法)应用拓展案例 1. 算法思维导图 以下是使用mermaid代码表示的Kadane算法的实现原理: #mermaid-svg-rI7hVAVsP1qtjZK7 {font-family:"tr…...
掌握NVM、NRM和NPM:Node.js开发的利器
**掌握NVM、NRM和NPM:Node.js开发的利器** 背景介绍:如何使用NVM:在Windows上安装NVM:在macOS上安装NVM:配置NVM:常用NVM命令: 如何使用NRM:安装NRM:配置全局NRM…...
Nacos 2.2.3 部署到linux
到https://github.com/alibaba/nacos/releases 下载编译后压缩包,如nacos-server-2.2.3.tar.gz这种,里面包含有nacos的jar包,不然启动不了 1.新建并初始化数据库 创建nacos数据库,执行mysql-schema.sql文件,这个文件…...
设计模式之十:状态模式
状态模式通过改变对象内部的状态来帮助对象控制自己的行为。 这是一张状态图,其中每个圆圈都是一个状态。 最简单,第一反应的实现就是使用一个变量来控制状态值,并在方法内书写条件代码来处理不同情况。 package headfirst.designpatterns.…...
Python 的运算符和语句(条件、循环、异常)基本使用指南
运算符的分类 算数运算符赋值运算符复合赋值运算符比较运算符逻辑运算符 算数运算符 运算符描述实例加1 1 输出结果为 2-减1 - 1 输出结果为 0*乘2 * 2 输出结果为 4/除10 / 2 输出结果为 5//整除9 // 4 输出结果为 2%取余9 % 4 输出结果为 1**指数2 ** 4 输出结果为 16&…...
文心一言 VS 讯飞星火 VS chatgpt (94)-- 算法导论9.2 3题
三、用go语言,给出 RANDOMIZED-SELECT的一个基于循环的版本。 文心一言: 在循环中实现 RANDOMIZED-SELECT 的一个版本可以如下所示。这个版本使用 Go 语言编写,利用随机化来选择一个元素,并在循环中不断地调整选择的元素&#x…...
React中组件通信01——props
React中组件通信01——props 1. 父传子——props1.1 简单例子——props1.2 props 可以传递任何数据1.2.1 传递数字、对象等1.2.2 传递函数1.2.3 传递模版jsx 2. 子传父 子传子——props2.1 父传子——传递函数2.2 子传父——通过父传子的函数实现2.3 优化 子传子(…...
接口测试——接口协议抓包分析与mock_L1
目录: 接口测试价值与体系常见的接口协议接口测试用例设计postman基础使用postman实战练习 1.接口测试价值与体系 接口测试概念 接口:不同的系统之间相互连接的部分,是一个传递数据的通道接口测试:检查数据的交换、传递和控制…...
四种常用的自动化测试框架
一直想仔细研究框架,写个流水账似的测试程序不难,写个低维护成本的测试框架就很难了,所以研究多种测试框架还是很有必要的,知道孰优孰劣,才能在开始编写框架的时候打好基础,今天读到了KiKi Zhao的翻译文章&…...
51c自动驾驶~合集58
我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留,CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制(CCA-Attention),…...
通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...
MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...
Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...
零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)
本期内容并不是很难,相信大家会学的很愉快,当然对于有后端基础的朋友来说,本期内容更加容易了解,当然没有基础的也别担心,本期内容会详细解释有关内容 本期用到的软件:yakit(因为经过之前好多期…...
springboot整合VUE之在线教育管理系统简介
可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生,小白用户,想学习知识的 有点基础,想要通过项…...
Java 与 MySQL 性能优化:MySQL 慢 SQL 诊断与分析方法详解
文章目录 一、开启慢查询日志,定位耗时SQL1.1 查看慢查询日志是否开启1.2 临时开启慢查询日志1.3 永久开启慢查询日志1.4 分析慢查询日志 二、使用EXPLAIN分析SQL执行计划2.1 EXPLAIN的基本使用2.2 EXPLAIN分析案例2.3 根据EXPLAIN结果优化SQL 三、使用SHOW PROFILE…...
day51 python CBAM注意力
目录 一、CBAM 模块简介 二、CBAM 模块的实现 (一)通道注意力模块 (二)空间注意力模块 (三)CBAM 模块的组合 三、CBAM 模块的特性 四、CBAM 模块在 CNN 中的应用 一、CBAM 模块简介 在之前的探索中…...
Linux信号保存与处理机制详解
Linux信号的保存与处理涉及多个关键机制,以下是详细的总结: 1. 信号的保存 进程描述符(task_struct):每个进程的PCB中包含信号相关信息。 pending信号集:记录已到达但未处理的信号(未决信号&a…...
从0开始一篇文章学习Nginx
Nginx服务 HTTP介绍 ## HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web )服务器传输超文本到本地浏览器的传送协议。 ## HTTP工作在 TCP/IP协议体系中的TCP协议上&#…...
