iis服务器助手广告
返回顶部
首页 > 资讯 > 操作系统 >Linux进程控制【进程程序替换】
  • 148
分享到

Linux进程控制【进程程序替换】

linux运维服务器云原生后端 2023-08-22 21:08:15 148人浏览 薄情痞子
摘要

✨个人主页: Yohifo 🎉所属专栏: Linux学习之旅 🎊每篇一句: 图片来源 🎃操作环境: CentOS 7.6 阿里云远程服务器 Good judgment comes fro

✨个人主页: Yohifo
🎉所属专栏: Linux学习之旅
🎊每篇一句: 图片来源
🎃操作环境: CentOS 7.6 阿里云远程服务器

  • Good judgment comes from experience, and a lot of that comes from bad judgment.
    • 好的判断力来自经验,其中很多来自糟糕的判断力。

上帝的指纹



🌇前言

子进程 在被创建后,共享的是 父进程 的代码,如果想实现自己的逻辑就需要再额外编写代码,为了能让 子进程 执行其他任务,可以把当前 子进程 的程序替换为目标程序,此时需要用到 linux 进程程序替换相关知识

子进程 替换为其他程序后,无法再执行原有程序,但 进程 始终为同一个

火爆全网的 ChatGTP 能否替换 “人类” ?

机器人


3D9;️正文

1、为何要进行程序替换?

在学习相关函数前,先要弄清楚为何要进行程序替换?

  • 将运行中的程序看作一个 任务处理平台
  • 由我们发出指令,交给 任务处理平台 去完成
  • 因为每次发出的指令都可能不相同,所以 任务处理平台 中的代码不能固化
  • 为了解决这个问题,任务处理平台 可以通过创建子进程,让子进程完成对应指令
  • 子进程实现对应指令依赖于程序替换

总结 程序替换的目的是让子进程帮我们执行特定任务

就像汽车拥有各种各样的轮胎,如越野时需要换上路面兼容性更好、更耐造的越野胎;日常家用时,舒适性更好、胎噪更小的轮胎显然就更合适了,针对不同的使用场景替换不同的轮胎,程序替换时也是这么个意思,执行特定任务

轮胎分类
shell 外壳中的 bash 就是一个任务处理平台,当我们发出指令,如 lspwdtouch 等指令时后,bash 会创建子进程,将其替换为对应的指令程序并执行任务,就能实现各种指令

bash

进程程序替换图解

  • Linux 中的指令都是用 C语言 写的可执行程序,所以可以进行替换
  • bash 运行后,输入 指令 本质上就是在进行程序替换

进程替换

关于简易版 bash 的实现方法,将在下篇文章中揭晓


2、七大替换函数

进程程序替换函数共有七个,其中六个都是在调用函数6,因此函数6 execve 才是真正的系统级接口

函数介绍

各种替换函数间的关系

函数调用关系
这些函数都属于 exec 替换家族,所以它们的返回值都一样

注意: 这七个函数只有在程序替换失败后才会有返回值,返回 -1,程序替换成功后不返回

程序都已经替换成功,后续代码也都将被替换,所以成功后的返回值也就没意义了

2.1、函数1 execl

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

#include int execl(const char* path, const char* arg, ...);

函数解读

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

图解

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

#include #include 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、函数2 execv

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

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

函数解读

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

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

#include #include  //exit 函数头文件#include #include #include 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;}

正常运行的情况

正常运行
错误运行的情况,改变 path

execv("/usr/bin", argv); //故意提供错误路径

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

顺序表传递

2.3、函数3 execlp

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

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

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

函数解读

  • 返回值:替换失败返回 -1
  • 参数1:待替换程序名,如 lspwdclear
  • 参数2~N:可变参数列表,为命令的选项

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

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

#include #include  //exit 函数头文件#include #include #include 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));  else    printf("子进程替换失败 exit_code:%d\n", WEXITSTATUS(status));  return 0;}

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

2.4、函数4 execvp

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

#include int execvp(const char* file, char* const argv[]);

函数解读

  • 返回值:替换失败返回 -1
  • 参数1:待替换程序名,需要位于 PATH
  • 参数2:待替换程序名及其命名构成的 指针数组
#include #include  //exit 函数头文件#include #include #include 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;}

结果
假若参数1 file 的路径不在 PATH 中,程序会替换错误

execvp("a.out", argv);

错误结果
如果想替换自己写的程序,那么只需要将路径添加至 PATH 中即可

2.5、函数5 execle

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

#include 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 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、函数6 execve

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

比如

  • execl 相当于将链式信息转化为 argv 表,供 execve 参数2使用
  • execlp 相当于在 PATH 中找到目标路径信息后,传给 execve 参数1使用
  • execleenvp 最终也是传给 execve 中的参数3
#include 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、函数7 execvpe

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

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

函数解读

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

结果


3、补充

最后再补充一些关于程序替换的知识

3.1、函数名记忆

七大替换函数按 程序名+选项 传递方式可以分为两组

  • 列表:execlexeclpexecle
  • 顺序:execvexecvpexecveexecvpe

可以看出,列表传递中必有 l,顺序传递则必有 v,函数名中字符的含义如下

  • exec 该函数隶属于程序替换家族
  • llist,列表传递
  • vvector,顺序传递
  • p 表示 PATH,根据程序名自动在 PATH 中查找
  • e 则是 environ,是否手动传递环境变量表

3.2、替换现象

子进程程序替换后,并不会创建新进程,而是对原有程序中的 数据代码 进行修改,可以通过替换以下程序观察

#include #include using namespace std;int main(){  while(1)  {    cout << "程序替换成功";    cout << " PID:" << getpid() << "   PPID:" << getppid() << endl;    sleep(1);  }  return 0;}

子进程
可以看到在进行程序替换后,子进程和待替换程序为同一个进程

  • 这就表明程序替换并不是进程替换
  • 因为是同一个进程,所以对父进程没有任何影响,体现了进程间的独立性

在子进程执行程序替换前,子进程和父进程共享一份只读区域的数据,但因为发生了程序替换,触发 写时拷贝 机制,令子进程读取另一块区域的数据

  • 写时拷贝 在只读数据区也能触发,因为不能影响到父进程

替换


🌆总结

以上就是本篇关于 Linux 进程程序替换的相关内容了,在本文中,我们知道了进行程序替换的目的,学习使用了程序替换相关的七大函数,最后还观察了程序替换后的神奇现象,在学完这些知识后,我们就可以实现一个简单的 bash,体验一下在自己程序中输入指令操控 Linux 的奇妙体验

如果你觉得本文写的还不错的话,期待留下一个小小的赞👍,你的支持是我分享的最大动力!

如果本文有不足或错误的地方,随时欢迎指出,我会在第一时间改正


星辰大海

相关文章推荐

Linux进程控制【创建、终止、等待】

===============

Linux进程学习【进程地址】

Linux进程学习【环境变量】

Linux进程学习【进程状态】

Linux进程学习【基本认知】

感谢支持

来源地址:https://blog.csdn.net/weixin_61437787/article/details/129396972

--结束END--

本文标题: Linux进程控制【进程程序替换】

本文链接: https://www.lsjlt.com/news/378076.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Linux进程控制【进程程序替换】
    ✨个人主页: Yohifo 🎉所属专栏: Linux学习之旅 🎊每篇一句: 图片来源 🎃操作环境: CentOS 7.6 阿里云远程服务器 Good judgment comes fro...
    99+
    2023-08-22
    linux 运维 服务器 云原生 后端
  • 【Linux】Linux进程控制及程序替换
    🍎作者:阿润菜菜 📖专栏:Linux系统编程 进程控制及程序替换 进程创建fork的用法进程调用fork,内核做了什么fork函数的返回值问题fork创建失败的原因 进程等待进程等待是什么...
    99+
    2023-08-24
    linux 运维 服务器
  • 【Linux】进程的程序替换
    文章目录 1. 程序替换1.创建子进程的目的是什么?2.了解程序是如何进行替换的3. 程序替换的基本原理当创建进程的时候,先有进程数据结构,还是先加载代码和数据?程序替换是整体替换,不是局部替换execl 返回值 4. 替换函...
    99+
    2023-08-22
    linux 运维 服务器
  • 【Linux】Linux进程控制 --- 进程创建、终止、等待、替换、shell派生子进程的理解…
    柴犬: 你好啊,屏幕前的大帅哥or大美女,和我一起享受美好的今天叭😃😃😃 文章目录 一、进程创建1.调用fork之后,内核都做了什么?2.如何...
    99+
    2023-09-08
    linux 运维 服务器
  • Linux--进程控制
    前言:         这篇文章主要是讲解Linux下的进程控制,我们会学习到进程等待,进程程序替换, 微型shell,重新认识shell运行原理 。最后也编写了一个属于我们自己的shell,尽管功能不够齐全,但是还是感觉挺有意思,挺好玩的...
    99+
    2023-09-16
    linux 运维 服务器
  • 【Linux】进程控制
    文章目录 一、进程创建1、再谈 fork 函数2、fork 函数返回值3、写时拷贝4、fork 常规用法5、fork 调用失败原因 二、进程终止1、进程退出码2、进程退出的情况3、进程退出的方法 三、进程等待1、为什么要进行...
    99+
    2023-08-22
    linux 运维 服务器
  • Linux进程控制
    Linux进程控制 进程创建fork函数初识fork函数返回值写时拷贝fork常规用法fork调用失败的原因 进程终止进程退出场景进程常见退出方法_exit函数exit函数 ...
    99+
    2023-09-22
    linux 运维 服务器 后端
  • Linux如何实现进程替换
    这篇文章将为大家详细讲解有关Linux如何实现进程替换,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。Linux 进程替换(exec函数)实现代码  # include<...
    99+
    2023-06-09
  • 深剖 Linux 进程控制
    目录 传统艺能😎pc 指针🤔为什么要写时拷贝?🤔退出码🤔exit 与 _exit🤔slab 分配器ᾑ...
    99+
    2023-09-07
    linux 服务器 进程
  • C语言进程程序替换的实现详解
    目录进程程序替换替换原理替换函数替换函数名称助记进程程序替换 替换原理 使用fork创建子进程后执行的是和父进程相同的程序,但是那样并没有多大的意义,子进程往往会“程序替...
    99+
    2024-04-02
  • 【看表情包学Linux】进程的概念 | 进程控制块 PCB | 父进程与子进程 | 进程 ID | task_struct
      🤣 爆笑教程 👉 《看表情包学Linux》👈 猛戳订阅  🔥 💭 写在前面:本章我们将带着大家深入理解 "进程" 的概念,"进程" 这个概念其实使我们一直...
    99+
    2023-10-23
    linux 运维 服务器 冯诺依曼架构
  • 《Linux从练气到飞升》No.20 Linux进程替换
     🕺作者: 主页 我的专栏C语言从0到1探秘C++数据结构从0到1探秘Linux菜鸟刷题集 😘欢迎关注:👍点赞🙌收藏✍️留言 🏇码字不易,你的&#...
    99+
    2023-09-15
    linux java 服务器
  • Linux进程控制的示例分析
    这篇文章将为大家详细讲解有关Linux进程控制的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。Linux进程控制详解及实例常用函数:fork() 通过复制调用进程来建立新的进程,是最基本的进程建立...
    99+
    2023-06-09
  • Linux进程控制【创建、终止、等待】
    ✨个人主页: Yohifo 🎉所属专栏: Linux学习之旅 🎊每篇一句: 图片来源 🎃操作环境: CentOS 7.6 阿里云远程服务器 Good judgment comes fro...
    99+
    2023-08-20
    linux 运维 服务器 进程 云原生
  • C语言控制进程之进程等待详解
    目录进程等待的必要进程等待的方法wait函数waitpid函数获取子进程退出信息进程等待的必要 当一个进程终止的时候,它的资源,比如说PCB,数据等不会被立马清理掉。它会保持在已经终...
    99+
    2024-04-02
  • Linux 进程管理中如何进行调度和进程切换
    这篇文章将为大家详细讲解有关Linux 进程管理中如何进行调度和进程切换,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。什么是调度按照某种调度算法,从进程的ready队列中选择进程给CPU。为...
    99+
    2023-06-15
  • Linux系统下的进程切换过程
    本篇内容介绍了“Linux系统下的进程切换过程”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!Linux内核下进程切换Linux切换并没有使用...
    99+
    2023-06-13
  • linux怎么查看java程序进程
    在Linux系统中,可以使用以下命令来查看Java程序的进程: 使用ps命令查看进程: ps -aux | grep java ...
    99+
    2024-03-14
    linux java
  • 【Linux】进程状态|优先级|进程切换|环境变量
    文章目录 1. 运行队列和运行状态2. 进程状态3. 两种特殊的进程僵尸进程孤儿进程 4. 进程优先级5. 进程切换进程特性进程切换 6. 环境变量的基本概念7. PATH环境变量8....
    99+
    2023-09-02
    linux 运维 服务器
  • Java程序控制逻辑—流程控制
    目录1前言2条件语句2.1if2.2switch3循环语句3.1while3.2do-while3.3for3.4for-in4循环控制4.1break4.2continue5码农洞...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作