Linux - 进程控制(进程替换)
迪丽瓦拉
2025-06-01 01:56:43
0

0.引入

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

就是为了让子进程帮我执行特定的任务

让子进程执行父进程的一部分代码
如果子进程想执行一个全新的程序代码呢? 那么就要使用进程的程序替换

为什么要有程序替换?

也就是说子进程想执行一个全新的程序代码!

这份代码看似是子进程的代码,其实也是父进程的代码,只是父进程通过id的值进行判断,让子进程运行。

1.替换原理

用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数 以执行另一个程序。

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

2.替换函数

其实有六种以exec开头的函数,统称exec函数:

使用man手册查看相关exec函数

execl最后的...参数,表示可变参数列表,可以给C函数传递任意的参数

execl

int execl(const char *path, const char *arg, ...);
  • path:新程序的路径和名称。

  • arg0 ~ argn:新程序的命令行参数列表,以 NULL 结尾。arg0 表示新程序的名称,arg1 ~ argn 表示新程序的各个参数。

为什么只看到了begin()... end...为什么不显示呢?

执行程序替换,新的代码和数据都被加载了,后续的代码属于老代码,直接被替换了,没机会执行了

并且程序替换是整体替换,不能局部替换!!!

ls是磁盘上的可执行程序,execl调用了ls-la

把当前的代码和数据,从execl到磁盘中进行替换

问题:进程的程序替换,有没有创建新的进程呢?

没有!!

为什么呢?很简单,因为我只是把一个新的程序加载到我们当前进程所对应的代码和数据段,我就让CPU去调度当前进程,就可以跑起来了。其中,我们并没有创建新的进程。当前的进程PID没有变化

这是站在进程的角度来看的

那么站在程序的角度呢? -- 这个程序被加载到内存里了

所以,我们也可以说execl是加载器

问题:当创建进程的时候,先加载进程数据结构,还是先加载代码和数据?

在创建进程时,操作系统通常会先创建进程数据结构,然后再加载代码和数据。

这里代码,子进程里的execl替换代码,会影响父进程吗?

不会,程序替换只会影响调用进程,进程具有独立性

子进程加载新程序的时候,是需要进行程序替换的,发生写时拷贝(子进程执行的可是全新的程序,新的代码,写时拷贝在代码区也可以发生)

execl函数调用失败,会发生什么?

我们写一个代码,让程序故意发生错误

父进程获取子进程退出码

接下来开始熟悉所有的接口

execv

 int execv(const char *path, char *const argv[]);

execv() 函数接受两个参数。第一个参数是要执行的程序的路径,第二个参数是传递给新程序的命令行参数。这些参数以一个字符串数组的形式传递给函数。

注意:当execv调用失败的话,程序继续执行

execlp

int execlp(const char *file, const char *arg, ...);

其中,file 参数表示要执行的可执行文件的名称,arg 参数表示传递给该可执行文件的命令行参数,最后的参数为可变参数列表,表示该命令行参数以 NULL 结束。

execlp 函数会在 PATH 环境变量指定的路径中查找可执行文件,因此无需指定可执行文件的完整路径。如果 PATH 环境变量中有多个路径,则 execlp 函数会按照路径的顺序查找可执行文件。

execlp("ls","ls","-a","-l","-n",NULL);

这里的两个ls分别是什么意思?

第一个是系统环境变量路径ls,第二个是ls指令

等同于以下代码

execle

 int execle(const char *path, const char *arg,..., char * const envp[]);

该函数接收以下参数:

  • path:要执行的程序的路径名。

  • arg0:新程序的第一个参数,通常是新程序的名称。

  • arg1~argn:新程序的参数列表。

  • envp:新程序的环境变量数组。

该函数的返回值是一个整数,如果成功执行,则永远不会返回。如果出现错误,则会返回-1,并设置errno来指示错误的类型。

envp[]数组是自定义环境变量

execvpe

函数原型如下:

int execvpe(const char *file, char *const argv[], char *const envp[]);

file 参数是要执行的可执行文件的路径,argv 参数是一个指向参数列表的指针数组,envp 参数是一个指向环境变量列表的指针数组。

execvpe 函数首先会搜索 PATH 环境变量中指定的目录,找到可执行文件后,它会用新的进程替换当前进程,并开始执行新的程序。在新的进程中,参数和环境变量都被设置成 argv 和 envp 中指定的值。

execvpe 函数和其他 exec 系列函数的主要区别在于它会搜索 PATH 环境变量中指定的目录来查找可执行文件,并且可以设置环境变量。

execve

函数原型如下:

int execve(const char *filename, char *const argv[], char *const envp[]);

filename 参数是要执行的可执行文件的路径,argv 参数是一个指向参数列表的指针数组,envp 参数是一个指向环境变量列表的指针数组。

execve 函数会用新的进程替换当前进程,并开始执行新的程序。在新的进程中,参数和环境变量都被设置成 argv 和 envp 中指定的值。

execve 函数不会搜索 PATH 环境变量中指定的目录来查找可执行文件,它只会使用 filename 参数中指定的路径来查找可执行文件。如果指定的文件路径不是一个可执行文件,那么该函数会返回一个错误。

事实上,只有execve是真正的系统调用,其它五个函数最终都调用 execve,所以execve在man手册第2节,其它函数在man手册第3节。这些函数之间的关系如下图所示。下图exec函数族 一个完整的例子:

3.调用自定义程序

上面都是执行命令。能否执行我自己写的程序呢?

用C语言调用C++写的可执行程序

可以发现PID是一模一样的,替换了C++程序,没有创建新的进程

接下来尝试在C++程序中获取环境变量

获取环境变量。C++的cout如果环境变量不存在,就什么都不会打印,所以要进行判断,如果没有就打印NULL

修改下C程序,在C程序中自定义环境变量表,使用execle函数

问题:C语言调用C++可以调用到环境变量吗?

修改下C++代码,获取环境变量

C++自己写的里有PATH,但没有环境变量

观察下C语言的

C语言调用的有MYENV 但是没有PATH

调用的环境变量是覆盖式写入,会覆盖老的环境变量PATH,所以看不到PATH

传系统环境变量

C语言提供的环境变量表,二级指针,environ

此时MYENV就没有了,系统的环境变量出现了

系统环境变量和自定义环境变量同时传递

利用putenv添加到系统环境变量里,依赖头文件#include

putenv将MYENV传到系统变量里,这样调用系统变量,就可以将自定义和系统变量一块传递给替换程序

环境变量具有全局属性,可以被子进程继承下去!
就是通过execle函数传递的环境变量!
使用export MYENV = You can see me   也可以

4.exec函数解释

1.这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。
2.如果调用出错则返回-1
3.所以exec函数只有出错的返回值而没有成功的返回值。

5.命名理解

这些函数原型看起来很容易混,但只要掌握了规律就很好记。

l(list) : 表示参数采用列表
v(vector) : 参数用数组
p(path) : 有p自动搜索环境变量PATH
e(env) : 表示自己维护环境变量

exec调用举例如下:

#include 
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);
}

相关内容

热门资讯

linux入门---制作进度条 了解缓冲区 我们首先来看看下面的操作: 我们首先创建了一个文件并在这个文件里面添加了...
C++ 机房预约系统(六):学... 8、 学生模块 8.1 学生子菜单、登录和注销 实现步骤: 在Student.cpp的...
A.机器学习入门算法(三):基... 机器学习算法(三):K近邻(k-nearest neigh...
数字温湿度传感器DHT11模块... 模块实例https://blog.csdn.net/qq_38393591/article/deta...
有限元三角形单元的等效节点力 文章目录前言一、重新复习一下有限元三角形单元的理论1、三角形单元的形函数(Nÿ...
Redis 所有支持的数据结构... Redis 是一种开源的基于键值对存储的 NoSQL 数据库,支持多种数据结构。以下是...
win下pytorch安装—c... 安装目录一、cuda安装1.1、cuda版本选择1.2、下载安装二、cudnn安装三、pytorch...
MySQL基础-多表查询 文章目录MySQL基础-多表查询一、案例及引入1、基础概念2、笛卡尔积的理解二、多表查询的分类1、等...
keil调试专题篇 调试的前提是需要连接调试器比如STLINK。 然后点击菜单或者快捷图标均可进入调试模式。 如果前面...
MATLAB | 全网最详细网... 一篇超超超长,超超超全面网络图绘制教程,本篇基本能讲清楚所有绘制要点&#...
IHome主页 - 让你的浏览... 随着互联网的发展,人们越来越离不开浏览器了。每天上班、学习、娱乐,浏览器...
TCP 协议 一、TCP 协议概念 TCP即传输控制协议(Transmission Control ...
营业执照的经营范围有哪些 营业执照的经营范围有哪些 经营范围是指企业可以从事的生产经营与服务项目,是进行公司注册...
C++ 可变体(variant... 一、可变体(variant) 基础用法 Union的问题: 无法知道当前使用的类型是什...
血压计语音芯片,电子医疗设备声... 语音电子血压计是带有语音提示功能的电子血压计,测量前至测量结果全程语音播报࿰...
MySQL OCP888题解0... 文章目录1、原题1.1、英文原题1.2、答案2、题目解析2.1、题干解析2.2、选项解析3、知识点3...
【2023-Pytorch-检... (肆十二想说的一些话)Yolo这个系列我们已经更新了大概一年的时间,现在基本的流程也走走通了,包含数...
实战项目:保险行业用户分类 这里写目录标题1、项目介绍1.1 行业背景1.2 数据介绍2、代码实现导入数据探索数据处理列标签名异...
记录--我在前端干工地(thr... 这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 前段时间接触了Th...
43 openEuler搭建A... 文章目录43 openEuler搭建Apache服务器-配置文件说明和管理模块43.1 配置文件说明...