进程程序替换+简易版shell实现

news2024/11/28 8:45:15

索引

  • 进程程序替换
    • 如何进行程序替换
      • 不同程序替换函数之间的区别
      • 系统接口调用其他语言的函数
      • 替换函数execle
  • 简易版shell实现

进程程序替换

什么是进程程序替换?
指在一个正在运行的进程中,将原来的程序替换成新的程序的过程。
eg:如果我们想让fork创建出来的子进程执行全新的任务,此时就需要进程程序替换
为什么要进程程序替换呢?
我们一般在服务器设计的时候(linux编程的时候)往往需要子进程干两种事情

  1. 让子进程执行父进程的代码片段(服务器代码)
  2. 让子进程执行磁盘中的一个全新的程序,eg:通过我们的进程执行其他人写的进程代码等等,可以使得我们的c/c++代码调用c/c++/Python/Shell/Java等代码

进程程序替换的原理
在这里插入图片描述
假设此时父进程执行的是a.exe,fork创建子进程之后,原本子进程要继承父进程的代码和数据,页表等映射也都是一样的,此时将磁盘中的b.exe加载如内存结构,使得子进程重新建立页表的映射关系,谁执行程序替换就重新建立谁的映射(此时是子进程)
效果:此时做到了让父子进程分离,并且让子进程执行一个全新的程序,至于如何做到,是有OS系统中的系统调用接口完成的。

如何进行程序替换

替换函数一共有六种,先用 execl第一种函数举例子
在这里插入图片描述
我们要执行一个全新的程序,需要做如下几件事情

  1. 先找到这个程序在哪里
  2. 程序可能携带选项进行执行,也可以不携带,(也就是程序怎么执行的)我们需要明确告诉OS,我想怎么执行这个程序,要不要带选项
  3. eg:命令行怎么写ls -l -a 第二个参数就怎么填 “ls”, “-l”, "-a"并且参数的最后一定是NULL,表示【如何执行程序】参数传递完毕
    在这里插入图片描述

发现此时执行了第一个printf,然后执行了ls命令,但是第二个printf没有打印了,为什么?
一旦替换成功,是将当前进程的代码和数据全部替换,后面的printf是原来进程的代码,所以改代码早就被替换了,改代码不存在了。
所以该程序替换函数需要返回值吗?
int ret = execl(…)
一旦替换成功,就会执行新的进程代码,就不会有返回值
而失败的话,必然会顺着原来的进程代码执行,最多通过返回值得到是什么原因导致的替换失败!

引入子进程创建
子进程执行程序替换,会影响父进程吗?
不会,因为进程具有独立性
如何做到的?
数据层面发生写实拷贝,程序替换的时候,我们可以理解为,代码和数据都发生了写实拷贝完成父子的分离。

19   printf("我是父进程,我的PID是:%d\n",getpid());
 20   pid_t id = fork();
 21   if(id == 0) {
 22     //我们想让子进程执行全新的程序,以前是执行父进程的代码片段
 23     printf("我是子进程,我的PID是:%d\n", getpid());
 24     execl("/usr/bin/ls","ls","-a","-l",NULL);
 25     printf("子进程替换进程失败\n");
 26     exit(1);//只要执行了exit,意味着,execl系列的函数失败了
 27 
 28   }
 29   //一定是父进程                                                                                                                            
 30   int status = 0;
 31   printf("我是父进程,我的PID: %d,我准备等子进程啦!\n",getpid());
 32   int ret = waitpid(id,&status,0);//阻塞式等待子进程
 33   if(ret == id) {
 34     sleep(1);
 35     printf("父进程等待成功!子进程的退出码是: %d  \n",(status>>8)&0XFF);
 36   }
 37   return 0;
 38 }

在这里插入图片描述

不同程序替换函数之间的区别

int execl(const char *path, const char *arg, …);
int execv(const char *path, char *const argv[]);
execv VS execl
二者几乎是一样的,只有传参方式的区别,execl传参的方式是list列表传参,execv是vector数组传参

在这里插入图片描述

int execvp(const char *file, char *const argv[]);
函数名中有p和v表示使用该函数时,可以不带路径,并且函数第二个参数传参的时候是vector传参

系统接口调用其他语言的函数

调用c++函数
在这里插入图片描述

调用Python

在这里插入图片描述

替换函数execle

int execle(const char *path, const char *arg, …,char *const envp[]);
函数名中的e表示环境变量
第三个参数既可以自己传自定义的环境变量
也可以传系统定义的环境变量,二者有所区分

在这里插入图片描述
上述代码中:mycomd.cc调用了getenv函数,打印出环境变量,而我们在mtproc.cc中先用execl调用mycomd,发现此时只有环境变量PATH那一行代码被打印出来了,到MYPATH这行代码时,就无法再运行下去了,因为此时在系统中没有环境变量MYPATH。
在这里插入图片描述
如图所示,如果我们导出环境变量变量的话,此时发现都可以打印出来了。
但是如果我们用execle函数自定义导入环境变量的话。

在这里插入图片描述
如果此时导入自定义变量,发现PATH系统自带的环境变量打印不出,说明添加环境变量给目标进程,是覆盖式的,只要传入自定义的环境变量,那么原来的环境变量,就失去作用了。
在这里插入图片描述
在这里插入图片描述
如果execle传入的是系统定义的环境变量,那么我们export导入的环境变量还是有用的。

问题:为什么程序替换会有那么多借口?
为了适配更多的应用场景
execve为什么是单独的?

int execve(const char *path, char *const argv[], char *const envp[]);
只有这个函数是系统接口,上面的函数都是对这个函数的封装
总结:
对于替换函数

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

简易版shell实现

shell的大致原理,以ls为例
shell从用户读入字符串,shell建立一个新的进程,然后在那个进程中运行ls程序并等待ls进程结束,然后shell读取新的一行输入,建立一个新进程,在这个进程中运行程序,并等待这个进程结束。所以要写一个shell,需要循环以下过程:

  1. 获取命令行
  2. 解析命令行
  3. 建立一个子进程
  4. 替换子进程
  5. 父进程等待子进程退出
    仅仅支持一些简单的命令
1 #include<stdio.h>
    2 #include<string.h>
    3 #include<stdlib.h>
    4 #include<unistd.h>
    5 #include<sys/types.h>
    6 #include<sys/wait.h>
    7 
    8 #define SEP " "
    9 #define NUM 1024
   10 #define SIZE 128
   11 char command_line[NUM];
   12 char *command_args[SIZE];
   13 char env_buffer[NUM];//fou test 
   14 extern char*enviorn;
   15 
   16 int ChangDir(const char * new_path)
   17 {
   18   chdir(new_path);
   19   return 0;//成功
   20 
   21 }
   22 void putEnvInMyShell(char *new_env)                                                                                                       
   23 {
   24   putenv(new_env);
   25 }
   26 int main()
   27 {
   28   //shell本质上就是一个死循环
   29   while(1)
   30   {
   31     //1.显示提示符
   32     printf("[zjt@大帅比 当前目录]# ");
   33     fflush(stdout);//强制刷新屏幕,否则上述信息回储存在缓冲区中
   34     //2.获取用户输入
   35     memset(command_line,'\0',sizeof(command_line)*sizeof(char));
   36     fgets(command_line,NUM,stdin);//键盘,标准输入,stdin,获取的是c风格的字符串,'\0';
   37     command_line[strlen(command_line)-1] = '\0';//清空'\n'
   38     //3."ls -a -l -i" -> "ls" "-a" "-l" "-i"字符串切分
   39     command_args[0] = strtok(command_line,SEP);
   40     int index = 1;
   41     //给ls命令添加颜色
   42     if(strcmp(command_args[0]/*程序名*/,"ls") == 0)
   43       command_args[index++] = (char*)"--color=auto";
   44     //strtok截取成功,返回字符串起始地址
   45     //截取失败,返回NULL;
   46     while(command_args[index++] = strtok(NULL,SEP));
   47     //按照SEP进行字符串切割,第一次调用时,第一个参数指向要分割的字符串
   48     //以后每次调用第一个参数都指向NULL,表示继续分割上一次的剩余部分
   49     //最终根据分割字符分割字符串
   50     //TODO,编写后面的逻辑,内建命令
   51     if(strcmp(command_args[0], "cd") == 0 && command_args[1] != NULL)
   52     {
   53       ChangDir(command_args[1]);//让调用方进行路径切换,父进程
   54       continue;                                                                                                                           
   55 
   56     }
   57     if(strcmp(command_args[0], "export") == 0 && command_args[1] != NULL)
   58     {
   59       //目前,环境变量信息在command_line,会被清空
   60       //此处我们需要保存一下环境变量内容
   61       strcpy(env_buffer,command_args[1]);
   62       putEnvInMyShell(env_buffer);
   63       continue;
   64 
   65     }
   66     //5.创建进程,执行
   67     pid_t id = fork();
   68     if(id == 0)
   69     {
   70       //child
   71       //6.程序替换
   72       execvp(command_args[0],/*我们保存的要执行的程序名*/command_args);
   73       exit(1);
   74       //此时子进程调用一定失败
   75     }                                                                                                                                     
   76     int status = 0;
W> 77     pid_t ret = waitpid(id, &status, 0);
   78   //  if(ret > 0)
   79   //  {
   80   //    printf("子进程等待成功,进程退出码为:%d,退出信号为:%d\n",(status>>8)&0xFF,status&0xFF);
   81   //    
   82   //  }
   83   }
   84 }

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

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

相关文章

I IntelliJ IDEA 2023.1 最新解锁方式,支持java20,让Coding飞一会儿

IntelliJ IDEA 2023.1 最新变化 在 IntelliJ IDEA 2023.1 中&#xff0c;我们根据用户的宝贵反馈对新 UI 做出了大量改进。 我们还实现了性能增强&#xff0c;从而更快导入 Maven&#xff0c;以及在打开项目时更早提供 IDE 功能。 新版本通过后台提交检查提供了简化的提交流程…

Android不基于第三发依赖包解析shp文件(2)

接着上篇文章继续 2)Point (点)   一个 Point 由一对双精度坐标组成,存储顺序为 X,Y。    /*** PointGeometry记录读取* */static Geometry renderPointGeometry(byte[] recordContent,GeometryFactory geometryFactory) {int shapetype2

N轴机械臂的MDH正向建模,及python算法

目录 一、前言二、三维空间的坐标系变换三、MDH建模要点四、MDH的变换矩阵推导五、机械臂MDH的python模型六、python源码 一、前言 如果机器人工程师缺乏机器人学理论的支撑和足够的认识&#xff0c;那么随着机器人项目的深入推进&#xff0c;可能会越走越艰难&#xff0c;所谓…

【Lambda】集合的Lambda表达式

【Lambda】集合的Lambda表达式 【一】Stream的方法介绍【1】Stream里常用的方法【2】collect(toList()) & filter【3】map【4】flatMap【5】max&min【6】reduce 【二】常用案例总结【1】准备方法查询数据库获取List结果【2】取值【3】分组【4】去重【5】排序【6】list的…

uniapp制作水印相机给图片添加水印并且保存图片至本地

uniapp保存文件的三种方式 文件主要分为两大类&#xff1a; 1.代码包文件&#xff1a;代码包文件指的是在项目目录中添加的文件。 2.本地文件&#xff1a;通过调用接口本地产生&#xff0c;或通过网络下载下来&#xff0c;存储到本地的文件。 其中本地文件又分为三种&#…

【K8s】Service详解

文章目录 一、Service介绍1、认识Service2、kube-proxy的三种工作模式3、Service的类型 二、Service的使用1、实验数据准备2、Service类型为ClusterIP补充&#xff1a;Endpoints补充&#xff1a;负载分发策略 3、Service类型为HeadLiness3、Service类型为NodePort4、Service的类…

Ovito渲染技巧之体系边界识别

关注 M r . m a t e r i a l , \color{Violet} \rm Mr.material\ , Mr.material , 更 \color{red}{更} 更 多 \color{blue}{多} 多 精 \color{orange}{精} 精 彩 \color{green}{彩} 彩&#xff01; 主要专栏内容包括&#xff1a; †《LAMMPS小技巧》&#xff1a; ‾ \textbf…

性能、性能还是性能---下一代Android性能框架分析

性能优化的重要性和难度毋庸置疑&#xff0c;事实上&#xff0c;性能优化也是一个永无止境的游戏&#xff0c;总会发现有一些东西需要优化。但是不可避免的是&#xff0c;这也是一个边际效益递减的事情&#xff0c;项目或应用获得的收益在一定程度上也会逐渐降低。What’s new …

ReentrantLock 原理

目录 ReentrantLock 概述 ReentrantLock 的原理 什么是 AQS ? 获取锁资源(以⾮公平锁为例) tryAcquire addWaiter acquireQueued 释放锁资源 ⾮公平锁体现在哪⾥&#xff1f; 调试代码 总结 ReentrantLock 概述 ReentrantLock是Lock接⼝的默认实现&#xff0c;是⼀…

【Linux】-关于Linux的指令(下)

&#x1f496;作者&#xff1a;小树苗渴望变成参天大树 ❤️‍&#x1fa79;作者宣言&#xff1a;认真写好每一篇博客 &#x1f4a8;作者gitee:gitee &#x1f49e;作者专栏&#xff1a;C语言,数据结构初阶,Linux,C 文章目录 前言一、时间相关的指令二、cal指令三、find -name指…

Centos7 经典模式安装GBase 8s 图文教程

Centos7安装GBase 8s 图文教程 前言1.1 硬件要求1.2 系统要求1.3 虚拟 PC 软件1.4 安装及配置数据库服务器1.4.1 创建 gbasedbt 组和用户1.4.2 创建 GBase 8s 数据库安装目录1.4.3 上传并解压安装包1.4.4 执行安装 参考文献 前言 本篇博文分享如何在Centos7 x86架构的系统中安…

第2章-Selenium-API操作

目标&#xff1a; 1. 熟练应用八种元素定位方式 2. 掌握对元素和浏览器的操作方法 3. 掌握键盘鼠标的操作 4. 掌握元素等待的操作 5. 掌握下拉选择框、 警告框和滚动条的操作 6. 掌握如何切换frame框架和多窗口 7. 掌握如何实现窗口截图 1.元素定位 1. 掌握id、 name、 cla…

【leetcode】622 二叉树的最大宽度

题目描述 给你一棵二叉树的根节点 root &#xff0c;返回树的 最大宽度 。 树的 最大宽度 是所有层中最大的 宽度 。 每一层的 宽度 被定义为该层最左和最右的非空节点&#xff08;即&#xff0c;两个端点&#xff09;之间的长度。将这个二叉树视作与满二叉树结构相同&#…

MATLAB 之 M 文件和程序控制结构(顺序、选择、循环)

文章目录 一、M 文件1. M 文件的建立与打开1.1 建立新的 M 文件1.2 打开已有的 M 文件 2. M 文件的分类 二、 程序控制之顺序结构1. 数据的输入2. 数据的输出3. 程序的暂停 三、程序控制之选择结构1. if 语句2. switch 语句3. try 语句 四、程序控制之循环结构1. for 语句2. wh…

畸变像差校正技术(畸变相位补偿)发展现状及其仿真实验研究-技术汇总-Matlab代码

▒▒本文目录▒▒ 一、引言二、物理方法2.1 基于二次曝光法的畸变像差补偿仿真实验验证 三、数值方法3.1 基于主成分分析法的畸变像差补偿仿真实验验证3.2 基于标准多项式拟合法的畸变像差校正仿真实验验证3.3 基于最小二乘拟合法的畸变像差校正仿真实验验证3.4 基于泽尼克多项…

Simulink 自动代码生成电机控制——永磁同步电机参数获取

目录 前言 极对数测量 电阻测量&#xff08;Rs&#xff09; 电感测量&#xff08;Ld和Lq&#xff09; 磁链测量 总结 前言 在建模之前或者需要更换一个新电机&#xff0c;需要获取目标电机的电气参数&#xff0c;如果参数不对&#xff0c;对于电流环参数的整定&#xff0…

2023/05/08~14 刷题记录

A - Plus and Multiply 题意&#xff1a; 题解&#xff1a; 题目说可以将集合里面的数字 *a 或者 b 操作&#xff0c;并将新的值放进集合中&#xff0c;首先想到 dfs 暴力求解&#xff0c;但是太暴力了&#xff0c;直接 时间超限 。通过观察我们可以知道&#xff0c;要求 n 是否…

前端模块化

什么是模块化 事实上模块化开发最终目的是将程序划分成一个个小的结构在这个结构中编写属于自己的逻辑代码&#xff0c;有自己的作用域&#xff0c;不会影响到其他的结构这个结构可以将自己希望暴露的变量、函数、对象等导出给其他结构使用。可以通过某种方式&#xff0c;导入…

电脑软件:国内最好用解压缩软件 7-Zip 新版本发布

5 月 9 日消息&#xff0c;7-Zip 是一款拥有极高压缩比的的开源压缩软件&#xff0c;支持 LZMA2 等 7 种开源算法&#xff0c;以及支持较广泛的压缩格式 .7z&#xff0c;体积不大&#xff08;1.5MB&#xff09;、功能完善&#xff0c;除了默认 UI 不符合国内审美外几乎没什么可…

Oracle数据库安装教程,并实现公网远程连接【内网穿透】

✨ 目录 &#x1f388; 前言&#x1f388; 1. 数据库搭建&#x1f388; 2. 内网穿透&#x1f388; 2.1 安装cpolar内网穿透&#x1f388; 2.2 创建隧道映射 &#x1f388; 3. 公网远程访问&#x1f388; 4. 配置固定TCP端口地址&#x1f388; 4.1 保留一个固定的公网TCP端口地址…