【C++探索学习】第十九弹——进程替换:深入解析操作系统中的进程替换机制

Linux学习笔记:

https://blog.csdn.net/2301_80220607/category_12805278.html?spm=1001.2014.3001.5482

前言:

在Linux操作系统中,进程替换(Process Replacement)是一个重要的概念,它允许程序通过系统调用用另一个程序替换当前进程的执行内容。这个操作通常是通过exec系列系统调用实现的。进程替换使得一个进程可以在不改变进程ID(PID)的情况下,执行不同的程序。理解和掌握exec系列函数对于深入了解Linux进程管理、进程间通信和系统编程非常重要。

本文将详细讲解Linux中进程替换的概念、exec系列函数的使用方法、相关的系统调用、常见的用法示例,以及进程替换的实际应用场景。同时还会提供丰富的代码示例,帮助大家更好地理解这些函数的使用。

目录

1. 什么是进程替换?

进程替换的核心特点:

2. exec系列函数

2.1 execve() 函数

函数原型:

示例代码:使用 execve() 执行一个程序

2.2 execvp() 函数

函数原型:

示例代码:使用 execvp() 执行一个程序

2.3 execlp() 函数

函数原型:

示例代码:使用 execlp() 执行一个程序

2.4 execv() 函数

函数原型:

示例代码:使用 execv() 执行一个程序

2.5 execl() 函数

函数原型:

示例代码:使用 execl() 执行一个程序

3. exec系列函数的常见错误

3.1 返回值与错误处理

3.2 exec的限制

4. 总结


1. 什么是进程替换?

在Linux中,进程替换是指一个进程通过调用exec系列函数来替换其当前的代码、数据、堆栈等内存区域,进而加载并执行新的程序。换句话说,进程替换使得一个正在运行的进程不再执行原来的程序,而是执行另一个程序。

进程替换发生时,当前进程的内存空间会被新的程序映像所替换。需要注意的是,进程替换操作本身并不会改变进程的PID(进程ID)。也就是说,进程替换发生后,新的程序在执行时,进程ID仍然是原进程的PID。(结合我们之前所讲的内容,进程替换改变的不是进程本身,而是页表对应的物理地址)

进程替换的核心特点:

  • 内存空间替换:进程的内存(代码段、数据段、堆栈等)会被新的程序映像替换。
  • 进程ID不变:即使进程的执行内容发生了变化,进程的PID不会改变。
  • 文件描述符继承:除非显式关闭,进程的文件描述符会被继承到新程序中,并且它们的状态(如文件指针位置)也会被继承。

这里内存空间替换更深层的其实是连接虚拟地址和物理地址之间页表指向的改变,关于页表的内容我们在前面讲进程地址空间时有讲过,作为拔高内容了解即可

进程替换通常与fork系统调用结合使用。在使用fork时,父进程会创建一个子进程,子进程继承父进程的状态(包括文件描述符、内存、环境变量等)。接着,子进程通过exec系列函数来替换自身的程序内容,执行新的任务。

2. exec系列函数

在Linux中,exec系列函数用于执行进程替换。它们会用新的程序替换当前进程的映像。exec系列函数有多个变种,常用的包括:

  • execve():最基础的系统调用,提供程序路径、参数数组和环境变量列表。
  • execvp():根据$PATH环境变量查找程序路径并执行。
  • execlp():与execvp()类似,但以参数列表的形式提供命令行参数。
  • execv():与execve()类似,但不查找$PATH,需要提供完整的程序路径。
  • execl():与execlp()类似,但以参数列表的形式传递命令行参数。

我们可以通过man函数手册来查看:

man exec

2.1 execve() 函数

execve()exec系列函数中最基础的函数,也是其他exec函数的底层实现。它允许一个进程加载并执行指定路径的程序,同时传递命令行参数和环境变量。

函数原型:
int execve(const char *pathname, char *const argv[], char *const envp[]);
  • pathname:要执行的程序的完整路径。
  • argv:一个字符串数组,包含传递给程序的命令行参数,最后必须以NULL结尾。
  • envp:一个字符串数组,包含程序的环境变量,最后也必须以NULL结尾。
示例代码:使用 execve() 执行一个程序
#include <stdio.h>
#include <unistd.h>int main() {char *args[] = {"ls", "-l", NULL};  // 命令行参数char *env[] = {"PATH=/bin", NULL};  // 环境变量printf("Before execve\n");if (execve("/bin/ls", args, env) == -1) {perror("execve failed");}printf("This will not be printed if execve is successful.\n");return 0;
}

在这个示例中,execve()会替换当前进程并执行ls -l命令。execve()调用成功后,原来的进程中的内容就会被替换,所以后续的printf语句将不会执行。

2.2 execvp() 函数

execvp()execve()的一个更高层的封装。它根据环境变量$PATH来查找可执行文件并执行。这意味着你只需要指定可执行文件的名称,而无需提供完整的路径。

函数原型:
int execvp(const char *file, char *const argv[]);
  • file:要执行的程序的名称,系统会根据$PATH查找该程序。
  • argv:命令行参数数组,最后必须以NULL结尾。
示例代码:使用 execvp() 执行一个程序
#include <stdio.h>
#include <unistd.h>int main() {char *args[] = {"ls", "-l", NULL};  // 命令行参数printf("Before execvp\n");// 使用 $PATH 查找可执行文件并执行execvp("ls", args);printf("This will not be printed if execvp is successful.\n");return 0;
}

在这个示例中,execvp()会根据$PATH查找ls命令并执行。如果execvp()调用成功,后续的printf语句将不会执行。

2.3 execlp() 函数

execlp()execvp()的一个变种,它允许你直接传递命令行参数,而不需要构建一个参数数组。

函数原型:
int execlp(const char *file, const char *arg, ...);
  • file:要执行的程序的名称,系统会根据$PATH查找该程序。
  • arg:程序的第一个参数,紧跟着是程序的其他参数,最后一个参数必须是NULL
示例代码:使用 execlp() 执行一个程序
#include <stdio.h>
#include <unistd.h>int main() {printf("Before execlp\n");// 使用 $PATH 查找可执行文件并执行execlp("ls", "ls", "-l", NULL);printf("This will not be printed if execlp is successful.\n");return 0;
}

在这个示例中,execlp()会查找ls命令并执行。与execvp()不同,execlp()是通过可变参数来传递命令行参数的,而不是使用数组。

运行结果:

2.4 execv() 函数

execv()execve()的一个封装,允许你提供程序路径和参数数组。它不通过$PATH查找程序,而是需要提供完整的程序路径。

函数原型:
int execv(const char *pathname, char *const argv[]);
  • pathname:要执行的程序的完整路径。
  • argv:命令行参数数组,最后必须以NULL结尾。
示例代码:使用 execv() 执行一个程序
#include <stdio.h>
#include <unistd.h>int main() {char *args[] = {"ls", "-l", NULL};  // 命令行参数printf("Before execv\n");// 使用完整路径执行命令execv("/bin/ls", args);printf("This will not be printed if execv is successful.\n");return 0;
}

在这个示例中,execv()会用完整路径/bin/ls来执行命令。如果execv()调用成功,后续的printf语句将不会执行。

运行结果:

2.5 execl() 函数

execl()execlp()的一个变种,使用可变参数的方式来传递命令行参数。

函数原型:
int execl(const char *pathname, const char *arg, ...);
  • pathname:要执行的程序的完整路径。
  • arg:程序的第一个参数,后面是其他参数,最后一个参数必须是NULL
示例代码:使用 execl() 执行一个程序
#include <stdio.h>
#include <unistd.h>int main() {printf("Before execl\n");// 执行命令并传递参数execl("/bin/ls", "ls", "-l", NULL);printf("This will not be printed if execl is successful.\n");return 0;
}

在这个示例中,execl()会执行/bin/ls命令,并传递参数-l

运行结果:

3. exec系列函数的常见错误

尽管exec系列函数非常强大,但它们也有一些常见的错误,需要注意。

3.1 返回值与错误处理

exec系列函数调用成功时,不会返回控制权给调用者。它们会替换当前进程的映像,新的程序开始执行。因此,如果exec调用成功,后面的代码将不会执行。如果调用失败,exec函数会返回-1,并设置errno,你可以通过perror()strerror()函数来输出错误信息。

常见的错误包括:

  • ENOENT:文件不存在。指定的可执行文件无法找到。
  • EACCES:权限不足。没有足够的权限来执行指定的文件。
  • ENOMEM:内存不足。系统无法为新的程序分配足够的内存。

3.2 exec的限制

  • exec替换时会丢失进程的原始状态。当前进程的栈、堆、全局变量等都会被新的程序内容覆盖。如果你需要保存某些状态(如打开的文件描述符),应在调用exec前进行适当的保存。
  • exec不会返回,如果你需要在exec失败时处理错误,必须在调用后检查返回值。

4. 总结

进程替换是Linux中一个非常重要的概念,exec系列函数提供了在运行时替换当前进程的能力。通过execve()execvp()execlp()execv()execl()等函数,我们可以灵活地执行不同的程序,而不需要创建新的进程。

进程替换通常与fork结合使用,fork创建一个新进程,而exec替换子进程的程序映像。这种模式广泛应用于Shell的实现、任务调度、进程间通信等领域。

函数描述
execve执行指定路径的程序,传递命令行参数和环境变量。
execvp根据$PATH查找可执行文件并执行,传递参数。
execlpexecvp类似,但以参数列表的形式传递命令行参数。
execvexecve类似,但不查找$PATH,需要提供完整路径。
execlexeclp类似,但以参数列表的形式传递命令行参数。

理解并熟练使用exec系列函数,是编写高效、灵活的系统程序的关键之一。希望本文通过丰富的代码示例和详细的解释,能够帮助你更好地掌握Linux中的进程替换机制。

本篇笔记:


感谢各位大佬观看,创作不易,还望各位大佬点赞支持!!!

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SSH克隆github项目

1、生成密钥 ssh-keygen -t rsa -C "你的邮箱xxx.com" 全程回车即可&#xff08;不用输入ras文件名及密码&#xff09;、为了方便下面的公钥查看 2、配置公钥 查看公钥内容 cat c:\Users\xxx\.ssh\id_rsa.pub(修改为自己的路径及名字) 将公钥内容复制并粘贴至…

ASP.NET Core 9.0 静态资产传递优化 (MapStaticAssets )

一、结论 &#x1f4a2;先看结论吧&#xff0c; MapStaticAssets 在大多数情况下可以替换 UseStaticFiles&#xff0c;它已针对为应用在生成和发布时了解的资产提供服务进行了优化。 如果应用服务来自其他位置&#xff08;如磁盘或嵌入资源&#xff09;的资产&#xff0c;则应…

电子信息工程自动化 单片机自动门控制系统设计

摘 要 伴随着社会经济的发展进步、科学技术的发展进步以及人民群众日常生活质量的逐渐提升&#xff0c;自动门开始全面进入人民群众的生活&#xff0c;逐渐发展成为了宾馆、大型超市、政府等当代建筑里必须配备的设备&#xff0c;是建筑自动智能化综合水平的主要标准之一。它具…

防火墙有什么作用

防火墙的作用&#xff1a;1. 提供网络安全防护&#xff1b;2. 实施访问控制和流量过滤&#xff1b;3. 检测和阻止恶意攻击&#xff1b;4. 保护内部网络免受未经授权的访问&#xff1b;5. 监控网络流量和安全事件&#xff1b;6. 支持虚拟专用网络&#xff08;VPN&#xff09;。防…

Cesium-地球材质-坡度

1. 创建viewer 创建viewer并添加地形 const viewer new Cesium.Viewer("cesiumContainer", {terrainProvider: await Cesium.CesiumTerrainProvider.fromIonAssetId(3956, {requestVertexNormals: true}) }); 2. 创建canvas色条 添加getColorRamp方法&#xff0…

企业网双核心交换机实现冗余和负载均衡(MSTP+VRRP)

MSTP&#xff08;多生成树协议&#xff09; 通过创建多个VLAN实例&#xff0c;将原有的STP、RSTP升级&#xff0c;避免单一VLAN阻塞后导致带宽的浪费&#xff0c;通过将VLAN数据与实例绑定&#xff0c;有效提升网络速率。 VRRP&#xff08;虚拟路由冗余协议&#xff09; 用…

VTK编程指南<三>:基于VTK入门程序解析来理解VTK基础知识

1、VTK入门程序 下面是一个完整的Vtk入门程序&#xff0c;我们基于这个程序来对VTK的基本知识进行一个初步了解。 #include <iostream>#include <vtkAutoInit.h> VTK_MODULE_INIT(vtkRenderingOpenGL2);// VTK was built with vtkRenderingOpenGL2 VTK_MODULE_INI…

React基础知识四 Hooks

什么是hooks&#xff1f; (coderwhy) hooks是react 16.8&#xff08;2019年&#xff09;出的新特性。 react有两种形式来创建组件——类式和函数式。在hooks之前类式组件就是react最主流的编程方式。 这个时候&#xff0c;函数式组件是非常鸡肋的&#xff0c;几乎没什么用。因…

TYUT计算机操作系统简答题

简述分组交换的优点。 高效灵活、迅速可靠&#xff0c;且各分组小&#xff0c;路由灵活&#xff0c;网络生存性能好。 解决 IPv4 地址紧缺有哪些方案&#xff1f; ① 使用代理服务器&#xff1b;② 使用地址转换&#xff1b;③ 升级到 IPv6。 网络适配器的作用是什么&#…

tomcat+jdbc报错怎么办?

1. 虽然mysql8.0以上的不用手动添加driver类&#xff0c;但是一旦加上driver类&#xff0c;就要手动添加了 不然会报找不到driver类的错误 2. java.lang.RuntimeException: java.sql.SQLException: No suitable driver found for jdbc:mysql://localhost:xXX?serverTimezoneU…

Qt6.8 QGraphicsView鼠标坐标点偏差

ui文件拖放QGraphicsView&#xff0c;src文件定义QGraphicsScene赋值给图形视图。 this->scene new QGraphicsScene();ui.graph->setScene(this->scene);对graphicview过滤事件&#xff0c;只能在其viewport之后安装&#xff0c;否则不响应。 ui.graph->viewport…

减少30%人工处理时间,AI OCR与表格识别助力医疗化验单快速处理

在医疗行业&#xff0c;化验单作为重要的诊断依据和数据来源&#xff0c;涉及大量的文字和表格信息&#xff0c;传统的手工输入和数据处理方式不仅繁琐&#xff0c;而且容易出错&#xff0c;给医院的运营效率和数据准确性带来较大挑战。随着人工智能技术的快速发展&#xff0c;…

工厂流水线上位机开发--USB转串口收不到数据,485通信时好时坏 不稳定问题

做过众多项目中&#xff0c;出现过很多次USB转485的返回值异常问题&#xff0c;自动化跑的好好的&#xff0c;一段时间后突然就查询不到返回值。 第一次出现时&#xff0c;上位机软件说是下位机问题&#xff0c;下位机说监控到返回数据了&#xff0c;是上位机问题&#xff0c;吵…

Mac曲线救国实现Bandizip右键一级菜单

一、前言 个人认为&#xff1a;Bandizip是Mac上最好用的压缩软件&#xff0c;没有之一。 在Mac系统上&#xff0c;学习版的Bandizip由于签名检验问题无法在访达右键的一级菜单显示 解压相关菜单。 有能力的&#xff0c;希望还是支持正版&#xff0c;找找优惠渠道应该100左右。…

矢量数据库

随着人工智能技术的深入发展&#xff0c;从图像识别到自然语言处理&#xff0c;从智能推荐到自动驾驶&#xff0c;AI的应用场景日益广泛。 而在这背后&#xff0c; 这种变革技术的核心数据概念是矢量。通过矢量化和大型语言模型(LLM) 的强大功能&#xff0c;生成式 AI 实现了其…

大模型学习有什么发展前景?

前景人工智能大模型是指拥有超大规模参数&#xff08;通常在十亿个以上&#xff09;、复杂计算结构的机器学习模型。它通常能够处理海量数据&#xff0c;完成各种复杂任务&#xff0c;如自然语言处理、图像识别等。 2024年政府工作报告提出“发展新质生产力”&#xff0c;并将…

2025澄迈漓岛音乐节品牌招商大会成功举行

——共谋音乐盛事&#xff0c;携手推动文化经济发展 12月6日&#xff0c;“2025澄迈漓岛音乐节品牌招商大会”&#xff08;以下简称“招商大会”&#xff09;在澄迈举行。本次大会由澄迈福山发展有限公司、福山咖啡文化风情镇旅游区联合主办&#xff0c;海南绿发投资有限公司承…

鲲鹏麒麟部署MongoDB4

本次部署采用Docker方式进行部署&#xff0c;服务器为鲲鹏服务器&#xff0c;CPU架构为ARM64&#xff0c;操作系统版本信息为 # cat /etc/kylin-release Kylin Linux Advanced Server release V10 (Tercel)找镜像 首先在https://hub.docker.com/网站上找到相关的额镜像&…

数据结构 (29)基于树的查找法

前言 数据结构中的基于树的查找法是一种高效的查找方法&#xff0c;它利用树形结构组织数据&#xff0c;使得查找过程能够迅速定位到目标元素。 一、树的基本概念 树是一种非线性结构&#xff0c;主要用来描述客观的层次结构关系。在树结构中&#xff0c;一个元素&#xff08;称…

前端框架的选择与反思:在简约与复杂之间寻找平衡

在当今互联网时代&#xff0c;前端开发已经成为web应用构建中不可或缺的一环。从最初的静态HTML页面&#xff0c;到如今复杂的单页应用&#xff08;SPA&#xff09;&#xff0c;前端技术的发展让我们见证了Web应用的蓬勃发展。然而&#xff0c;伴随着技术的进步&#xff0c;一个…