Linux操作系统2-进程控制3(进程替换,exec相关函数和系统调用)

ops/2024/11/28 10:55:13/

上篇文章:Linux操作系统2-进程控制2(进程等待,waitpid系统调用,阻塞与非阻塞等待)-CSDN博客

本篇代码Gitee仓库:Linux操作系统-进程的程序替换学习 · d0f7bb4 · 橘子真甜/linux学习 - Gitee.com

本篇重点:进程替换

目录

一. 什么是进程替换?

二. 进程替换函数常用的函数 

2.1 execl 

a 进程替换覆盖指定位置后面的代码

b 进程替换不会影响父进程 

2.2 execlp 

 2.3 execv/execvp

2.4 execle 

 2.5 execve 系统调用

 三. 进程替换总结

四. 进程替换可以执行任何后端语言!


一. 什么是进程替换?

        我们知道,使用fork函数可以创建子进程。我们创建子进程的目的是什么?

1 让子进程执行父进程的一部分代码(比如执行父进程处于磁盘中的代码)

2 我们希望让子进程执行一个全新的进程,去完成一个不同的功能

        我们让子进程去执行新程序的数据和代码 -> 进程的程序替换

二. 进程替换函数常用的函数 

常见的函数如下:

#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[]);

是不是有点眼花缭乱?具体使用如下

2.1 execl 

a 进程替换覆盖指定位置后面的代码

int execl(const char *path, const char *arg, ...);    //path    用于找到程序(程序的路径,ls的路径是 /usr/bin/ls)
//arg     这个命令是怎么执行的(比如ls命令的执行方式就是单独的 ls)
//...     这个命令需要的参数是什么(比如ls可以带参数 -a -l -i 等)//返回值,失败返回-1,并且设置错误码

我们使用execl去替换一个 ls命令。代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main()
{// 使用execl去替换ls完成  ls -a -l --color=autoprintf("process is running!\n");//第一个参数,要执行哪个路径,就填这个路径//第二个参数,程序怎么执行,就怎么填//后面的参数,执行这个程序需要带的参数,就一个一个地填这些参数//最好使用NULL结尾int n = execl("/usr/bin/ls", "ls", "-a", "-l", "--color=auto", NULL);//使用系统调用或者涉及底层的C库函数,需要判断错误返回perror("execl");//由于执行execl之后,代码被覆盖,以下的代码不会被运行!printf("process is running!\n");printf("process is running!\n");printf("process is running!\n");printf("process is running!\n");printf("process is running!\n");return 0;
}

编译运行结果如下:

 

        execl之后的代码没有被执行的原因是:进程替换的时候没有创建新进程,而是将指定的程序和代码加载到指定的位置

        这样会覆盖后面的代码,所以后面printf就不会执行了!

b 进程替换不会影响父进程 

        进程替换会覆盖指定位置后面的代码,不过我们在子进程中使用进程替换不会影响父进程的代码和数据。

       这个原因是父子进程之间有写实拷贝,由于父子进程之间的写实拷贝,一旦子进程尝试写入,OS就会给子进程开辟一份空间用于保存子进程的数据和代码。这样一来,子进程进程替换就不会影响父进程。

测试代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{// 使用execl去替换ls完成  ls -a -l --color=autoprintf("1 process is running!\n");pid_t id = fork();if (id == 0){int n = execl("/usr/bin/ls", "ls", "-a", "-l", "--color=auto", NULL);perror("execl");// 如果替换失败exit(-1);}// 由于写实拷贝,父进程不受子进程进程替换影响printf("2 process is running!\n");int status = 0;int subid = waitpid(id, &status, 0);if (subid > 0){printf("child exit code:%d child exit signal\n", (status >> 8) & 0xff, status & 0x7f);}return 0;
}

测试结果: 

 

2.2 execlp 

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

p:path,只要输入替换的程序,会自动去环境变量中进行查找。无需告诉路径

测试代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{// 使用execl去替换ls完成  ls -a -l --color=autoprintf("1 process is running!\n");pid_t id = fork();if (id == 0){//p:path,只要输入替换的程序,会自动去环境变量中进行查找。无需告诉路径execlp("ls", "ls", "-a", "-l", "--color=auto", NULL);perror("execl");// 如果替换失败exit(-1);}// 由于写实拷贝,父进程不受子进程进程替换影响printf("2 process is running!\n");int status = 0;int subid = waitpid(id, &status, 0);if (subid > 0){printf("child exit code:%d child exit signal\n", (status >> 8) & 0xff, status & 0x7f);}return 0;
}

测试结果:

 

 2.3 execv/execvp

//v:vector:可以将所有的执行参数放入数组,进行统一传入,不用可变参数传参 
int execv(const char *path, char *const argv[]);    // v:vector p:文件名
int execvp(const char *file, char *const argv[]);  

execv 举例:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{// 使用execl去替换ls完成  ls -a -l --color=autoprintf("1 process is running!\n");pid_t id = fork();if (id == 0){char * argv_[] = {"ls", "-a", "-l", "--color=auto", NULL}; execv("/usr/bin/ls", argv_);perror("execv");// 如果替换失败exit(-1);}// 由于写实拷贝,父进程不受子进程进程替换影响printf("2 process is running!\n");int status = 0;int subid = waitpid(id, &status, 0);if (subid > 0){printf("child exit code:%d child exit signal\n", (status >> 8) & 0xff, status & 0x7f);}return 0;
}

测试结果:

 

 execvp 加上了p:我们只需给出名字,会去环境变量中自动查找

测试代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{// 使用execl去替换ls完成  ls -a -l --color=autoprintf("1 process is running!\n");pid_t id = fork();if (id == 0){char * argv_[] = {"ls", "-a", "-l", "--color=auto", NULL}; execvp("ls", argv_);perror("execvp");// 如果替换失败exit(-1);}// 由于写实拷贝,父进程不受子进程进程替换影响printf("2 process is running!\n");int status = 0;int subid = waitpid(id, &status, 0);if (subid > 0){printf("child exit code:%d child exit signal\n", (status >> 8) & 0xff, status & 0x7f);}return 0;
}

测试结果:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{// 使用execl去替换ls完成  ls -a -l --color=autoprintf("1 process is running!\n");pid_t id = fork();if (id == 0){char * argv_[] = {"ls", "-a", "-l", "--color=auto", NULL}; execvp("ls", argv_);perror("execvp");// 如果替换失败exit(-1);}// 由于写实拷贝,父进程不受子进程进程替换影响printf("2 process is running!\n");int status = 0;int subid = waitpid(id, &status, 0);if (subid > 0){printf("child exit code:%d child exit signal\n", (status >> 8) & 0xff, status & 0x7f);}return 0;
}

测试结果

 

2.4 execle 

int execle(const char *path, const char *arg, ...,char *const envp[]);    //e:传入自定义环境变量,

e:传入自定义环境变量

我们定义另一个C程序mybin,让这个程序打印环境变量。然后我们在子进程中替换为mybin

这样我们的子进程就能够打印我们在execle函数中输入的环境变量了

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{// 使用execl去替换ls完成  ls -a -l --color=autoprintf("1 process is running!\n");pid_t id = fork();if (id == 0){//注意在最后加上NULLchar *const env_[] = {"AA=11", "BB=22", "CC=33", "YZC=Hello world!", NULL};// 加入命令行参数envexecle("./mybin", "./mybin", NULL, env_);perror("execle");// 如果替换失败exit(-1);}// 由于写实拷贝,父进程不受子进程进程替换影响printf("2 process is running!\n");int status = 0;int subid = waitpid(id, &status, 0);if (subid > 0){printf("child exit code:%d child exit signal\n", (status >> 8) & 0xff, status & 0x7f);}return 0;
}

运行结果如下:

 我们换成系统环境变量

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{// 使用execl去替换ls完成  ls -a -l --color=autoprintf("1 process is running!\n");pid_t id = fork();if (id == 0){//注意在最后加上NULLchar *const env_[] = {"AA=11", "BB=22", "CC=33", "YZC=Hello world!", NULL};// 加入命令行参数envextern char** environ;execle("./mybin", "./mybin", NULL, environ);perror("execle");// 如果替换失败exit(-1);}// 由于写实拷贝,父进程不受子进程进程替换影响printf("2 process is running!\n");int status = 0;int subid = waitpid(id, &status, 0);if (subid > 0){printf("child exit code:%d child exit signal\n", (status >> 8) & 0xff, status & 0x7f);}return 0;
}

运行结果: 

当然我们也能够在 命令行参数获取环境变量

 2.5 execve 系统调用

上面的exec函数都是对execve系统调用的封装

//2号手册,这个才是真正的系统调用(上面的都是函数封装)
int execve(const char *path, char *const argv[], char *const envp[]);

 

 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{// 使用execl去替换ls完成  ls -a -l --color=autoprintf("1 process is running!\n");pid_t id = fork();if (id == 0){// 注意在最后加上NULLchar *const env_[] = {"AA=11", "BB=22", "CC=33", "YZC=Hello world!", NULL};char *const argv_[] = {"./mybin"};execve("./mybin", argv_, env_);perror("execve");// 如果替换失败exit(-1);}// 由于写实拷贝,父进程不受子进程进程替换影响printf("2 process is running!\n");int status = 0;int subid = waitpid(id, &status, 0);if (subid > 0){printf("child exit code:%d child exit signal\n", (status >> 8) & 0xff, status & 0x7f);}return 0;
}

测试结果: 

 三. 进程替换总结

我们知道程序必须加载到内存中才能执行。这是由冯诺依曼计算机体系结构决定的。

在Linux中就是通过exec*函数来加载程序的!

那么是exec函数先加载还是main函数先执行?

exec函数先加载,然后main函数再执行

我们可以发现exec函数的参数和main函数的参数很像!

所以是exec函数先加载路径,参数,环境变量在执行main函数 

四. 进程替换可以执行任何后端语言!

myCppBin.cc

#include <iostream>
using namespace std;int main()
{cout << "Hello C++!" << endl;return 0;
}

mytest.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{// 使用execl去替换ls完成  ls -a -l --color=autoprintf("1 process is running!\n");pid_t id = fork();if (id == 0){// 注意在最后加上NULLchar *const env_[] = {"AA=11", "BB=22", "CC=33", "YZC=Hello world!", NULL};char *const argv_[] = {"./myCppBin"};//执行C程序//execve("./mybin", argv_, env_);//执行C++execve("./myCppBin", argv_, env_);perror("execve");// 如果替换失败exit(-1);}// 由于写实拷贝,父进程不受子进程进程替换影响printf("2 process is running!\n");int status = 0;int subid = waitpid(id, &status, 0);if (subid > 0){printf("child exit code:%d child exit signal\n", (status >> 8) & 0xff, status & 0x7f);}return 0;
}

 


http://www.ppmy.cn/ops/137329.html

相关文章

Elasticsearch ILM 索引生命周期管理讲解与实战

ES ILM 索引生命周期管理讲解与实战 Elasticsearch ILM索引生命周期管理:深度解析与实战演练1. 引言1.1 背景介绍1.2 研究意义2. ILM核心概念2.1 ILM的四个阶段2.1.1 Hot阶段2.1.2 Warm阶段2.1.3 Cold阶段2.1.4 Delete阶段3. ILM实战指南3.1 定义ILM策略3.1.1 创建ILM策略3.1.…

STM32-- 调试- 延时、编译空循环

编译对空循环的处理&#xff0c;会影响堵塞延时效果&#xff0c;具体怎么处理的还不知道&#xff0c;只知道结果和现象。 模拟串口输出字符&#xff0c;用到延时函数&#xff0c;同样的延时函数&#xff0c;会有正常和不正常输出的情况&#xff1b;具体现象如下&#xff0c; /…

vi/vim文件管理命令练习

一.练习要求 文件管理命令练习&#xff1a; &#xff08;1&#xff09;在/opt目录下创建一个临时目录tmp&#xff1b; &#xff08;2&#xff09;在临时目录下创建一个文件&#xff0c;文件名为a.txt&#xff1b;vi/vim练习&#xff1a; (1) 应用vi命令在/tmp文件夹下创建文…

QChart数据可视化

目录 一、QChart基本介绍 1.1 QChart基本概念与用途 1.2 主要类的介绍 1.2.1 QChartView类 1.2.2 QChart类 1.2.3QAbstractSeries类 1.2.4 QAbstractAxis类 1.2.5 QLegendMarker 二、与图表交互 1. 动态绘制数据 2. 深入数据 3. 缩放和滚动 4. 鼠标悬停 三、主题 …

ThingsBoard规则链节点:GCP Pub/Sub 节点详解

目录 引言 1. GCP Pub/Sub 节点简介 2. 节点配置 2.1 基本配置示例 3. 使用场景 3.1 数据传输 3.2 数据分析 3.3 事件通知 3.4 任务调度 4. 实际项目中的应用 4.1 项目背景 4.2 项目需求 4.3 实现步骤 5. 总结 引言 ThingsBoard 是一个开源的物联网平台&#xff…

Python3交叉编译arm-linux放入设备中运行方式

设置交叉编译环境 设置交叉编译工具链环境变量&#xff0c;告诉编译系统使用交叉编译工具链进行编译&#xff0c;而不是本地编译器。 export CROSS_COMPILEaarch64-linux-gnu- export ARCHarm64CROSS_COMPILE 指定交叉编译工具链的前缀&#xff0c;aarch64-linux-gnu- 表示你…

数据结构--B树

B树 B树原理实现 B树B*树 B树系列包括B树&#xff08;有些地方写成B-树&#xff0c;注意不要读成B减树&#xff0c;中间的 ‘-’ 是杠的意思&#xff0c;不是减号&#xff09;、B树、B 树&#xff0c;其中B树、B树是B树的改进优化&#xff0c;它们最常见的应用就是用于做索引。…

Kubernetes 分布式存储后端:指南

在 Kubernetes 中实现分布式存储后端对于管理跨集群的持久数据、确保高可用性、可扩展性和可靠性至关重要。在 Kubernetes 环境中&#xff0c;应用程序通常被容器化并跨多个节点部署。虽然 Kubernetes 可以有效处理无状态应用程序&#xff0c;但有状态应用程序需要持久存储来维…