【Linux】程序替换

news2025/1/11 2:40:41

       🔥🔥 欢迎来到小林的博客!!
      🛰️博客主页:✈️小林爱敲代码
      🛰️博客专栏:✈️Linux之路
      🛰️社区 :✈️ 进步学堂
      🛰️欢迎关注:👍点赞🙌收藏✍️留言

文章目录

  • 💖程序替换的概念
  • 💖程序替换的原理
  • 💖进程替换函数
    • execl
    • execlp
    • execle
    • execv
    • execvp
    • execve

💖程序替换的概念

目前,我们执行的子进程,都只能让子进程执行父进程的代码。那么我们想要让 子进程去执行一个全新的进程,那么这就是程序替换。 程序替换不会再创建新的进程,仅仅只是修改子进程的代码和数据。

💖程序替换的原理

进程替换的本质就是,替换代码和数据! 我们都知道子进程会继承父进程的代码和数据,而如果子进程的数据发生修改。那么父进程会写实拷贝一份数据,然后把拷贝的数据交给子进程。

在这里插入图片描述

而程序替换的本质,就是替换子进程的代码和数据!随后再更新一下页表的一些数据即可,也就是说,不用去修改PCB!只需要替换页表映射数据和代码的物理内存即可! 把子进程的代码和数据替换成全新进程的代码和数据,那么全新进程从哪里获取呢? 全新进程是从磁盘上获取的!然后把子进程的代码和数据替换成全新进程的即可。

在这里插入图片描述

但是,数据发生修改时进行了写实拷贝。但是子进程和父进程的代码是共享的!如果这样直接替换,那么父进程的代码不也被替换掉 ?所以:在正常情况下,子进程和父进程共用同一段代码!但是如果子进程发生了程序替换。那么父进程的代码同样会写实拷贝一份!因为进程之间是相互独立的。

所以正确的情况应该是如下图这样:

在这里插入图片描述

当然。页表也会进行调整。

💖进程替换函数

我们知道了进程替换是什么,进程替换的原理。那么怎么实际运用到我们的代码上呢?那么我们就需要介绍一些exce替换函数。

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

而我们exce替换函数有七种,其中有一种是系统调用。所以我们这里只介绍六种。

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

函数解释

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

函数命名规律

这些函数看起来好复杂好难记,其实是有规律的。

  • (list) : 表示参数采用列表
  • v(vector) 参数用数组
  • p(path) : 有p自动搜索环境变量PATH
  • e(env) : 表示自己维护环境变量
函数名参数格式是否带路径是否使用当前环境变量
execl列表不是
execlp列表
execle列表不是不是,需要自己组装环境变量
execv数组不是
execvp数组
execve数组不是不是,需要自己组装环境变量

接下来我们依次演示这6个函数。

execl

int execl(const char *path, const char *arg, …)。第一个参数传的是文件所在路径,第二个是一个可变参数列表。根据的在命令行学的命令挨个填。

比如你想程序执行 ls -a -1,你只需要:

execl "文件路径","命令1","命令2" ..... NULL

我们写一个myload程序演示一瞎。

#include<stdio.h>
#include<unistd.h>


int main()
{
  unsigned int pid = fork();
  if(pid == 0)
  {
    //child
    printf("i am child\n");
    execl("/usr/bin/ls","ls","-a","-1",NULL);//execl "文件路径","命令1","命令2" .....NULL
    printf("hhhhhhhhhhhhhh\n");
    printf("hhhhhhhhhhhhhh\n");
    printf("hhhhhhhhhhhhhh\n");
    printf("hhhhhhhhhhhhhh\n");
    printf("hhhhhhhhhhhhhh\n");
    printf("hhhhhhhhhhhhhh\n");
    exit(0);//如果execl调用失败,那么子进程直接终止。
  }
  sleep(1);
  printf("i am father\n");

  return 0;
}

然后我们运行一下这个程序。

在这里插入图片描述

我们可以发现子进程并没有打印下列一大串的hhhhhhhhhhhhhhh。因为子进程的代码已经被替换成ls的代码了。

execlp

execlp 和 execl 可以说百分之95的相似度了。知识execlp可以不用带全路径,只要是存在在环境变量中的,就可以直接输入命令找到环境变量。

代码演示:

#include<stdio.h>
#include<unistd.h>


int main()
{
  unsigned int pid = fork();
  if(pid == 0)
  {
    //child
    printf("i am child\n");
    execl("ls","ls","-a","-1",NULL);//execl "环境变量","命令1","命令2" .....NULL
    printf("hhhhhhhhhhhhhh\n");
    printf("hhhhhhhhhhhhhh\n");
    printf("hhhhhhhhhhhhhh\n");
    printf("hhhhhhhhhhhhhh\n");
    printf("hhhhhhhhhhhhhh\n");
    printf("hhhhhhhhhhhhhh\n");
    exit(0);//如果execl调用失败,那么子进程直接终止。
  }
  sleep(1);
  printf("i am father\n");

  return 0;
}

结果还是一样

在这里插入图片描述

注意,一定是要是环境变量中的。否则无法找到对应的路径。

execle

这个需要自己组装环境变量,那么我们再写一个myexe.c的程序。

#include<stdio.h>


int main()
{
  //就遍历一下环境变量
  extern char ** environ;
  int i = 0;
  while(environ[i])
  {
    printf("%s\n",environ[i]);
    ++i;
  }

  return 0;
}

这个程序很简单,就是打印一下环境变量。

然后我们运行这个程序

在这里插入图片描述

我们可以发现,它打印了所有的环境变量,那么我们再来修改一下我们的 myload文件。

#include<stdio.h>
#include<unistd.h>


int main()
{
  unsigned int pid = fork();
  if(pid == 0)
  {
    //child
    printf("i am child\n");
    char *env[] = 
    {
      "A = AAAAAAAAAAAAAAAAA",
      "B = BBBBBBBBBBBBBBBBB",
      "C = CCCCCCCCCCCCCCCCC",
      NULL 
    };  
    execle("./myexe","myexe",NULL,env);//传自己组装的环境变量
    printf("hhhhhhhhhhhhhh\n");
    printf("hhhhhhhhhhhhhh\n");
    printf("hhhhhhhhhhhhhh\n");
    printf("hhhhhhhhhhhhhh\n");
    printf("hhhhhhhhhhhhhh\n");
    printf("hhhhhhhhhhhhhh\n");
    exit(0);
  }
  sleep(1);
  printf("i am father\n");

  return 0;
}

这时候我们运行myload试试。

在这里插入图片描述

这时候我们的子进程就打印了父进程组装的环境变量。

execv

而v则代表数组,也就是说传一个数组进去。

#include<stdio.h>
#include<unistd.h>


int main()
{
  unsigned int pid = fork();
  if(pid == 0)
  {
    //child
    printf("i am child\n");
    char *avg[] = 
    {
      "ls","-a","-1",NULL 
    };//命令数组

    execv("/usr/bin/ls",avg);//传数组进去

 
    printf("hhhhhhhhhhhhhh\n");
    printf("hhhhhhhhhhhhhh\n");
    printf("hhhhhhhhhhhhhh\n");
    printf("hhhhhhhhhhhhhh\n");
    printf("hhhhhhhhhhhhhh\n");
    printf("hhhhhhhhhhhhhh\n");
    exit(0);
  }
  sleep(1);
  printf("i am father\n");

  return 0;
}

execvp

和execlp一样,只不过把列表换成数组。

#include<stdio.h>
#include<unistd.h>


int main()
{
  unsigned int pid = fork();
  if(pid == 0)
  {
    //child
    printf("i am child\n");
    char *avg[] = 
    {
      "ls","-a","-1",NULL 
    };
    execvp("ls",avg);//传一个数组
    printf("hhhhhhhhhhhhhh\n");
    printf("hhhhhhhhhhhhhh\n");
    printf("hhhhhhhhhhhhhh\n");
    printf("hhhhhhhhhhhhhh\n");
    printf("hhhhhhhhhhhhhh\n");
    printf("hhhhhhhhhhhhhh\n");
    exit(0);
  }
  sleep(1);
  printf("i am father\n");

  return 0;
}

execve

同理,和execle一样,只不过列表变成了数组传递。

#include<stdio.h>
#include<unistd.h>


int main()
{
  unsigned int pid = fork();
  if(pid == 0)
  {
    //child
    printf("i am child\n");
    char *env[] = 
    {
      "A = AAAAAAAAAAAAAAAAA",
      "B = BBBBBBBBBBBBBBBBB",
      "C = CCCCCCCCCCCCCCCCC",
        NULL 
    };

    char *avg[] = 
    {
      "myexe",NULL 
    };
    execve("/myexe",avg,env);
    printf("hhhhhhhhhhhhhh\n");
    printf("hhhhhhhhhhhhhh\n");
    printf("hhhhhhhhhhhhhh\n");
    printf("hhhhhhhhhhhhhh\n");
    printf("hhhhhhhhhhhhhh\n");
    printf("hhhhhhhhhhhhhh\n");
    exit(0);
  }
  sleep(1);
  printf("i am father\n");

  return 0;
}

以内建方式进行运行!

不创建子进程,让父进程自己执行

chdir
{
      "myexe",NULL 
    };
    execve("/myexe",avg,env);
    printf("hhhhhhhhhhhhhh\n");
    printf("hhhhhhhhhhhhhh\n");
    printf("hhhhhhhhhhhhhh\n");
    printf("hhhhhhhhhhhhhh\n");
    printf("hhhhhhhhhhhhhh\n");
    printf("hhhhhhhhhhhhhh\n");
    exit(0);
  }
  sleep(1);
  printf("i am father\n");

  return 0;
}

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

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

相关文章

51单片机地震监测语音报警提示系统MPU6050TTS报警

实践制作DIY- GC0153--- 地震监测语音报警提示系统 基于51单片机设计------- 地震监测语音报警提示系统 二、功能介绍&#xff1a; STC89C52单片机TTS语音播报模块MPU-6050角度传感器LED灯显示一个按键 1.获取MPU-6050角度数据&#xff0c;然后根据角度数据计算出0~10级的地震…

B站w_rid,qv_id加密分析

本文仅供学习交流&#xff0c;只提供关键思路不会给出完整代码&#xff0c;严禁用于非法用途&#xff0c;若有侵权请联系我删除&#xff01; 网站地址&#xff1a;aHR0cHM6Ly9zZWFyY2guYmlsaWJpbGkuY29tL2FsbD92dD03OTExMjUwNSZrZXl3b3JkPSVFNyU4OCVBQyVFOCU5OSVBQg 关键字搜…

Flutter 布局构建

文章目录 一、布局类组件简介二、理解 Flutter 布局约束三、线性布局&#xff08;Row和Column&#xff09;1. 主轴和纵轴2. Row3. Column4. 特殊情况 四、弹性布局&#xff08;Flex 和 Expanded&#xff09;1. Flex2. Expanded 五、流式布局&#xff08;Wrap 和 Flow&#xff0…

记录一次Ubuntu系统安装tenforflow

电脑已有版本 Ubuntu 20.04GCC 9.4.0CUDA 11.3 需要指定版本的python库 tensorflow 2.6.0numpy 1.19.2matplotlib 3.4.0keras 2.6.0protobuf 3.19.6 我的环境

LeetCode刷题 | 583. 两个字符串的删除操作、72. 编辑距离

583. 两个字符串的删除操作 给定两个单词 word1 和 word2 &#xff0c;返回使得 word1 和 word2 相同所需的最小步数。 每步 可以删除任意一个字符串中的一个字符。 示例 1&#xff1a; 输入: word1 "sea", word2 "eat" 输出: 2 解释: 第一步将 "…

Flutter Bloc组件buildWhen的妙用

在Flutter中当状态发生改变的时候,Widget会重新build刷新页面。但是当状态发生改变的时候后,我们指向让有关联的Widget重绘,与之无关的Widget保持不变,比如对于登录页面,有用户名和密码两个组件:如下图。 构建代码如下: 当我们输入用户名的时候,仅仅希望_UserNameInp…

C语言王国探险记之转义字符+结构语句

王国探险记系列 文章目录&#xff08;4&#xff09; 一&#xff0c;什么是转义字符 1.1转义字符的重要性&#xff1f; 二&#xff0c;了解一下转义字符有那些 三&#xff0c;精讲转义字符 3.1转义字符\n 3.2转义字符\? 3.3转义字符 \‘和\" 3.3转义字符 \\ 3.4转义字…

【云原生丶Kubernetes】Kubernetes初体验

人生若只如初见&#xff0c;何事秋风悲画扇。 前言 Kubernetes 是目前最流行的容器编排工具之一&#xff0c;由Google开发并维护。它提供了完整的容器编排解决方案&#xff0c;包括自动化部署、资源管理和调度、服务发现和负载均衡等功能。 然而&#xff0c;对于初学者来说&a…

Android Studio实现内容丰富的安卓民宿酒店预订平台

如需源码可以添加q-------3290510686&#xff0c;也有演示视频演示具体功能&#xff0c;源码不免费&#xff0c;尊重创作&#xff0c;尊重劳动。 项目编号106 1.开发环境 android stuido jdk1.8 eclipse mysql tomcat 2.功能介绍 安卓端&#xff1a; 1.注册登录 2.查看民宿 3.民…

缔造网络世界的奇迹:OSI参考模型的魔力之旅

文章目录 一 OSI参考模型1.1 OSI参考模型简介1.2 OSI参考模型的下特性和优点1.3 OSI参考模型的层次结构1.4 OSI参考模型层次间的关系以及数据封装 二 OSI参考模型的层次结构详解2.1 物理层2.2 数据链路层2.3 网络层2.3.1 网络层地址2.3.2 路由协议与可路由协议2.3.3 面向连接和…

激光SLAM(二):点云拟合和平面参数化

点云拟合和平面参数 一、平面拟合&#xff1a;特征值法和SVD法二、平面参数化1&#xff09;Hesse形式2&#xff09;球坐标3&#xff09;最近点4) 单位四元数 三、直线拟合 一、平面拟合&#xff1a;特征值法和SVD法 平面方程如下&#xff0c;n是法向量&#xff0c;d是截距 当…

python协程—asyncio模块

目录 什么是协程 协程的特点 为什么使用协程 asyncio协程模块 多任务的协程 总结 什么是协程 协程&#xff08;Coroutine&#xff09;是一种并发编程的技术&#xff0c;它允许程序在执行过程中暂停和恢复。协程可以看作是一种特殊的函数&#xff0c;它可以暂停执行&#…

快速安装Kibana,head插件以及IK分词器

安装Kibana Kibana是基于node.js的,安装这个之前要先安装node.js 1 压缩包下载地址: Kibana 6.2.4 | Elastic 2.将下载的Kibana压缩包解压到任意一个没有中文没有空格的目录下,进入Kibana安装目录下的config目录&#xff0c;修改kibana.yml文件&#xff0c;添加Elasticsearch服…

钳形表校准装置 “定点输出”方式

装置具有“定点输出”方式&#xff0c;通过点击触摸屏&#xff0c;直接设置所需输出的量值&#xff0c;装置将全自动切换至最佳量程输出&#xff0c;使得校准数字式仪表变得非常方便。特别是在设定交流电压/电流输出时&#xff0c;直接同时输入幅值和频率即可&#xff0c;使用十…

【计算机视觉】对比学习综述(自己的一些理解)

对比loss 对比学习的 loss&#xff08;InfoNCE&#xff09;即以最 大化互信息为目标推导而来。其核心是通过计算样本表示间的距离&#xff0c;拉近正样本&#xff0c; 拉远负样本&#xff0c;因而训练得到的模型能够区分正负例。 具体做法为&#xff1a;对一个 batch 输入的图…

Tensorflow入门(1)——深度学习框架Tesnsflow入门 环境配置 认识Tensorflow

目录 一、环境配置1.Anaconda以及Tensorflow的安装2.CONDA环境安装3.测试是否成功 二、认识TensorFlow1.图 Graph()2.会话 Session()3.张量 Tensor&#xff08;1&#xff09;张量的形状&#xff08;2&#xff09;数据类型张量的动态形状与静态形状 4.张量操作&#xff08;1&…

No9.精选前端面试题,享受每天的挑战和学习

文章目录 从recat源码角度说明下setState流程同源策略限制什么行为computed和watch的区别http和https的区别vue中的route和router的异同点&#xff0c;区别 从recat源码角度说明下setState流程 在 React 的源码中&#xff0c;setState 方法是用于更新组件的状态&#xff08;st…

java中预览pdf或者图片

GetMapping("preview1")ApiOperation(value "根据文件名实现预览功能")public void previewFile1( HttpServletResponse response) throws IOException {String filePath"C:\\Users\\Harbor Lau\\Desktop\\哈哈\\""11月燃油费(公开).pdf&q…

照片如何恢复?试试这3个简单操作!

我的电脑里保存了很多照片&#xff0c;都是对我来说意义重大的&#xff0c;但最近电脑内存不足了&#xff0c;在清理内存时&#xff0c;我不小心把照片也清空了&#xff0c;这些照片应该如何恢复呢&#xff1f;谁有什么好的方法恢复电脑里的照片吗&#xff1f; 可能很多人都会选…

【UnityDOTS 小知识】RequireMatchingQueriesForUpdate与RequireForUpdate

RequireMatchingQueriesForUpdate与RequireForUpdate 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 例如&#xff1a;第一章 Python 机器学习入门之pandas的使用 一、RequireMatchingQueriesForUpdate 将这个特性放置于System类上…