linux进程控制【程序替换】

目录

前言:

1.替换原理

​编辑 2.替换函数

2.1函数 execl

2.2函数 execv

2.3函数 execlp

2.4函数 execvp

2.5函数 execle

2.6函数 execve

2.7函数 execvpe



前言:

前面我们介绍了进程控制中的创建,退出等待,本章节我们将继续介绍一下进程替换,以上就是关于 Linux进程控制(创建、终止、等待) 的相关知识了,我们学习了 子进程 是如何被创建的,创建后又是如何终止的,以及 子进程 终止 父进程 需要做些什么,有了这些知识后,在对 进程 进行操作时能更加灵活和全面。

1.替换原理

fork 创建子进程后执行的是和父进程相同的程序 ( 但有可能执行不同的代码分支 ), 子进程往往要调用一种 exec 函数
以执行另一个程序。当进程调用一种 exec 函数时 , 该进程的用户空间代码和数据完全被新程序替换 , 从新程序的启动
例程开始执行。调用 exec 并不创建新进程 , 所以调用 exec 前后该进程的 id 并未改变

 2.替换函数

 其实有六种以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函数只有出错的返回值而没有成功的返回值。

命名理解: 

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

 

 

2.1函数 execl

 首先是最简单的替换函数 execl

 

#include <unistd.h>int execl(const char* path, const char* arg, ...);

函数解读

  • 返回值:替换失败返回 -1
  • 参数1:待替换程序的路径,如 /usr/bin/ls
  • 参数2:待替换程序的名称,如 ls
  • 参数3~N:待替换程序的选项,如 -a -l等,最后一个参数为 NULL,表示选项传递结束
  • ... 表示可变参数列表,可以传递多个参数

注意: 参数选项传递结束或不传递参数,都要在最后加上 NULL,类似于字符串的 '\0'

 

#include <stdio.h>
#include <unistd.h>int main()
{//execl 函数printf("程序替换前,you can see me\n");int ret = execl("/usr/bin/ls", "ls", "-a", "-l", NULL);//程序替换多发生于子进程,也可以通过子进程的退出码来判断是否替换成功if(ret == -1)printf("程序替换失败!\n");printf("程序替换后,you can see me again?\n");return 0;
}

 可以看出,函数 execl 中的 命令+选项+NULL 是以 链式 的方式进行传递的,方便理解。

 

2.2函数 execv

 替换函数 execv 是以顺序表 vector 的方式传递 参数2~N 的

 

函数解读

  • 返回值:替换失败返回 -1
  • 参数1:待替换程序的路径,如 /usr/bin/ls
  • 参数2:待替换程序名及其命名构成的 指针数组,相当于一张表

 

注意: 虽然 execv 只需传递两个参数,但在创建 argv 表时,最后一个元素仍然要为 NULL 

#include <stdio.h>
#include <stdlib.h> //exit 函数头文件
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{//execv 函数pid_t id = fork();if(id == 0){printf("子进程创建成功 PID:%d   PPID:%d\n", getpid(), getppid());char* const argv[] = {"ls","-a","-l",NULL};	//argv 表,实际为指针数组execv("/usr/bin/ls", argv);printf("程序替换失败\n");exit(-1); //如果子进程有此退出码,说明替换失败}int status = 0;waitpid(id, &status, 0); //父进程阻塞等待if(WEXITSTATUS(status) != 255){printf("子进程替换成功,程序正常运行 exit_code:%d\n", WEXITSTATUS(status));}else{printf("子进程替换失败,异常终止 exit_code:%d\n", WEXITSTATUS(status));}return 0;
}

运行结果:

与 execl 函数不同,execv 是以表的形式进行参数传递的

 

2.3函数 execlp

可能有的人觉得写 path 路径很麻烦,还有可能会写错,那么能否换成 自动挡 替换呢?

答案是可以的,execlp 函数在进行程序替换时,可以不用写 path 路径

 

#include <unistd.h>int execlp(const char* file, const char* arg, ...);

execlp 就像是 execl 的升级版,可以自动到 PATH 变量中查找程序

注意: 只能在环境变量表中的 PATH 变量中搜索,如果待程序路径没有在 PATH 变量中,是无法进行替换的

#include <stdio.h>
#include <stdlib.h> //exit 函数头文件
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{//execlp 函数pid_t id = fork();if(id == 0){printf("you can see me\n");execlp("ls", "ls", "-a", "-l", NULL); //程序替换printf("you can see me again?");exit(-1);}int status = 0;waitpid(id, &status, 0);  //等待阻塞if(WEXITSTATUS(status) != 255)printf("子进程替换成功 exit_code:%d\n", WEXITSTATUS(status));elseprintf("子进程替换失败 exit_code:%d\n", WEXITSTATUS(status));return 0;
}

使用 execlp 替换程序更加方便,只要待替换程序路径位于 PATH 中,就不会替换失败

2.4函数 execvp

 execv 加个 p 也能实现自动查询替换,即 execvp

 

#include <unistd.h>int execvp(const char* file, char* const argv[]);

函数解读

  • 返回值:替换失败返回 -1
  • 参数1:待替换程序名,需要位于 PATH 中
  • 参数2:待替换程序名及其命名构成的 指针数组

 

#include <stdio.h>
#include <stdlib.h> //exit 函数头文件
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{//execvp 函数pid_t id = fork();if(id == 0){printf("子进程创建成功 PID:%d   PPID:%d\n", getpid(), getppid());char* const argv[] = {"ls","-a","-l",NULL};execvp("ls", argv);printf("程序替换失败\n");exit(-1); //如果子进程有此退出码,说明替换失败}int status = 0;waitpid(id, &status, 0); //父进程阻塞等待if(WEXITSTATUS(status) != 255){printf("子进程替换成功,程序正常运行 exit_code:%d\n", WEXITSTATUS(status));}else{printf("子进程替换失败,异常终止 exit_code:%d\n", WEXITSTATUS(status));}return 0;
}

 

 

2.5函数 execle

 e 表示 env 环境变量表,可以将自定义或当前程序中的环境变量表传给待替换程序

 

#include <unistd.h>int execl(const char* path, const char* arg, ..., char* const envp[]);

函数解读

  • 最后一个参数:替换成功后,待替换程序的环境变量表,可以自定义
char* const myenv[] = {"myval=100", NULL};  //自定义环境变量表execle("./other/CPP", NULL, myenv); //程序替换

替换为自己写的程序 CPP

 

//当前源文件为 test.cc 即 C++源文件
// .xx 后缀也可以表示 C++源文件
#include <iostream>using namespace std;extern char** environ;	//声明环境变量表int main()
{int pos = 0;//只打印5条while(environ[pos] && pos < 5){cout << environ[pos++] << endl;}return 0;
}

 

可以看到,程序 CPP 中的环境变量表变成了自定义环境变量,即只有一个环境变量 myval=100

改变 execle 最后一个参数,传入默认环境变量表

 extern char** environ;execle("./other/CPP", NULL, environ); //继承环境变量表

结论: 如果主动传入环境变量后,待替换程序中的原环境变量表将被覆盖

现在可以理解为什么在 bash 中创建程序并运行,程序能继承 bash 中的环境变量表了

在 bash 下执行程序,等价于在 bash 下替换子进程为指定程序,并将 bash 中的环境变量表 environ 传递给指定程序使用
其他没有带 e 的替换函数,默认传递当前程序中的环境变量表

 

 

2.6函数 execve

execve 是系统真正提供的程序替换函数,其他替换函数都是在调用 execve

比如

execl 相当于将链式信息转化为 argv 表,供 execve 参数2使用
execlp 相当于在 PATH 中找到目标路径信息后,传给 execve 参数1使用
execle 的 envp 最终也是传给 execve 中的参数3

 

#include <unistd.h>int execve(const char* filename, char* const argv[], char* const envp[]);

函数解读

  • 返回值:替换失败返回 -1
  • 参数1:待替换程序的路径
  • 参数2:待替换程序名及其参数组成的 argv 表
  • 参数3:传递给待替换程序的环境变量表

替换 ls -a -l 程序

extern char** environ;execve("/usr/bin/ls", argv, environ);

替换为自定义程序 CPP

extern char** environ;execve("./other/CPP", argv, environ);

 替换函数除了能替换为 C++ 编写的程序外,还能替换为其他语言编写的程序,如 JavaPythonPHP等等,虽然它们在语法上各不相同,但在 OS 看来都属于 可执行程序,数据位于 代码段 和 数据段,直接替换即可

系统级接口是不分语言的,因为不论什么语言最终都需要调用系统级接口,比如文件流操作中的 openclosewrite 等函数,无论什么语言的文件流操作函数都需要调用它们 

2.7函数 execvpe

对 execvp 的再一层封装,使用方法与 execvp 一致,不过最后一个参数可以传递环境变量表

#include <unistd.h>int execvpe(const char* file, char* const argv[], char* const envp[]);

函数解读

  • 返回值:替换失败返回 -1
  • 参数1:待替换程序名,需要位于 PATH 中
  • 参数2:待替换程序名及其命名构成的 指针数组
  • 参数3:传递给待替换程序的环境变量表
extern char** environ;execvpe("ls", argv, environ);

 

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

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

相关文章

8.JS中的== 操作符的强制类型转换规则

对于 来说&#xff0c;如果对比双方的类型不一样&#xff0c;就会进行类型转换。假如对比 x 和 y 是否相同&#xff0c;就会进行如下判断流程&#xff1a; 首先会判断两者类型是否相同&#xff0c;类型相同的话就比较两者的大小&#xff1b;类型不相同的话&#xff0c;就会进…

核心篇-OSPF技术之序(下)

文章目录 一. 实验专题1.1. 实验1&#xff1a;配置OSPF特殊区域1.1.1. 实验目的1.1.2. 实验拓扑图1.1.3. 实验步骤&#xff08;1&#xff09;配置IP地址&#xff08;2&#xff09;创建环回口&#xff08;3&#xff09;查看路由表&#xff08;4&#xff09;设置Stub区域&#xf…

2024/02/13

21 、C 22 、D 23、B 如果5先出栈那么1&#xff0c;2&#xff0c;3&#xff0c;4就已经入栈了&#xff0c;5出后4出&#xff0c;1要出栈必须先让3&#xff0c;2出栈&#xff0c;所以 不可能输出B 24、10&#xff0c;12&#xff0c;120 25、2&#xff0c;5 26、段错…

selenium定位元素报错:‘WebDriver‘ object has no attribute ‘find_element_by_id‘

Selenium更新到 4.x版本后&#xff0c;以前的一些常用的代码的语法发生了改变 from selenium import webdriver browser webdriver.Chrome() browser.get(https://www.baidu.com) input browser.find_element_by_id(By.ID,kw) input.send_keys(Python)目标&#xff1a;希望通…

【Python--网络编程之TCP三次握手】

&#x1f680; 作者 &#xff1a;“码上有前” &#x1f680; 文章简介 &#xff1a;Python开发技术 &#x1f680; 欢迎小伙伴们 点赞&#x1f44d;、收藏⭐、留言&#x1f4ac; Python网络编程之[TCP三次握手] 往期内容代码见资源&#xff0c;效果图如下一、实验要求二、协…

嵌入式内核链表list_head,如何管理不同类型节点的实现

在Linux内核中&#xff0c;提供了一个用来创建双向循环链表的结构 list_head。虽然linux内核是用C语言写的&#xff0c;但是list_head的引入&#xff0c;使得内核数据结构也可以拥有面向对象的特性&#xff0c;通过使用操作list_head 的通用接口很容易实现代码的重用&#xff0…

P1219 八皇后 (dfs 表格坐标关系)

一个正常的dfs&#xff08;数据范围1-13&#xff09;&#xff0c;发现一条对角线上&#xff0c;分别符合和与差相等。因为有负数&#xff0c;这里我最开始开的是map&#xff0c;发现卡了最后一个点TLE&#xff0c;记录一下时间复杂度&#xff08; map&#xff0c;set的时间复杂…

Mysql——update更新数据的方式

注&#xff1a;文章参考&#xff1a; MySQL 更新数据 不同条件(批量)更新不同值_update批量更新同一列不同值-CSDN博客文章浏览阅读2w次&#xff0c;点赞20次&#xff0c;收藏70次。一般在更新时会遇到以下场景&#xff1a;1.全部更新&#xff1b;2.根据条件更新字段中的某部分…

同学,请实现一个扫码登录

马上要到春节了&#xff0c;小伙伴们的公司是不是已经可以申请请假调休呢&#xff1f;虽然今年刚入职没有年假(好像国家不是这么规定的&#xff0c;但也不好跟公司硬杠)&#xff0c;大小周的我已经攒了 7 天调休&#xff0c;也可以提前回家过年啦&#xff01; 即使是年底&…

LLM大模型常见问题解答(3)

简要描述下列概念在大语言模型中的作用 Transformer 架构Attention 机制预训练与微调过拟合和欠拟合 Transformer 架构 Transformer是一种基于自注意力机制的深度学习模型&#xff0c;它在论文“Attention Is All You Need”中首次提出。与此前流行的循环神经网络&#xff0…

sql语句学习(一)--查询

【有道云笔记】基本sql语句2—查询基础 数据库表结构 DROP TABLE IF EXISTS class; CREATE TABLE class (id int(11) NOT NULL AUTO_INCREMENT,class_num varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT 班级号,class_name varchar(255) CHARACTE…

Unity基础 -- 更新中(2.15)

Unity基础 文章目录 Unity基础3D数学 -- 基础一些方法插值运算三角函数坐标系 3D数学 -- 向量理解常用函数线性插值 3D数学 -- 四元数看向四元数计算 延迟函数协同程序 3D数学 – 基础 一些方法 float value01 Mathf.PI; // Π int value03 Mathf.Abs(-2); // 绝对值 int v…

Midjourney绘图欣赏系列(三)

Midjourney介绍 Midjourney 是生成式人工智能的一个很好的例子&#xff0c;它根据文本提示创建图像。它与 Dall-E 和 Stable Diffusion 一起成为最流行的 AI 艺术创作工具之一。与竞争对手不同&#xff0c;Midjourney 是自筹资金且闭源的&#xff0c;因此确切了解其幕后内容尚不…

springboot191教师工作量管理系统

简介 【 毕设 源码 推荐 javaweb 项目】 基于 springbootvue 的教师工作量管理系统&#xff08;springboot191&#xff09; 适用于计算机类毕业设计&#xff0c;课程设计参考与学习用途。仅供学习参考&#xff0c; 不得用于商业或者非法用途&#xff0c;否则&#xff0c;一切后…

【机器学习笔记】8 决策树

决策树原理 决策树是从训练数据中学习得出一个树状结构的模型。 决策树属于判别模型。 决策树是一种树状结构&#xff0c;通过做出一系列决策&#xff08;选择&#xff09;来对数据进行划分&#xff0c;这类似于针对一系列问题进行选择。决策树的决策过程就是从根节点开始&…

2.15学习总结

2.15 1.聪明的质监员&#xff08;二分前缀和&#xff09; 2.村村通&#xff08;并查集&#xff09; 3.玉蟾宫(悬线法DP) 4.随机排列&#xff08;树状数组逆序对问题&#xff09; 5.增进感情&#xff08;DFS&#xff09; 6.医院设置&#xff08;floyd&#xff09; 聪明的质监员…

《动手学深度学习(PyTorch版)》笔记8.7

注&#xff1a;书中对代码的讲解并不详细&#xff0c;本文对很多细节做了详细注释。另外&#xff0c;书上的源代码是在Jupyter Notebook上运行的&#xff0c;较为分散&#xff0c;本文将代码集中起来&#xff0c;并加以完善&#xff0c;全部用vscode在python 3.9.18下测试通过&…

C++数据结构与算法——双指针法

C第二阶段——数据结构和算法&#xff0c;之前学过一点点数据结构&#xff0c;当时是基于Python来学习的&#xff0c;现在基于C查漏补缺&#xff0c;尤其是树的部分。这一部分计划一个月&#xff0c;主要利用代码随想录来学习&#xff0c;刷题使用力扣网站&#xff0c;不定时更…

NLP_ChatGPT的RLHF实战

文章目录 介绍小结 介绍 ChatGPT 之所以成为ChatGPT&#xff0c;基于人类反馈的强化学习是其中重要的一环。而ChatGPT 的训练工程称得上是复杂而又神秘的&#xff0c;迄今为止&#xff0c;OpenAl也没有开源它的训练及调优的细节。 从 OpenAl已经公开的一部分信息推知&#xff…

计算机组成原理(2)-----存储芯片与CPU的连接

目录 一.单块存储芯片与CPU的连接 二.多块存储芯片与CPU的连接 1.位扩展 2.字扩展 &#xff08;1&#xff09;线选法 &#xff08;2&#xff09;译码器片选法 3.字位同时扩展 三.译码器相关 一.单块存储芯片与CPU的连接 如图所示是8*8位的芯片&#xff0c;总共8个存储…