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

这篇具有很好参考价值的文章主要介绍了进程程序替换+简易版shell实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

进程程序替换

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

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

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

如何进行程序替换

替换函数一共有六种,先用 execl第一种函数举例子
进程程序替换+简易版shell实现
我们要执行一个全新的程序,需要做如下几件事情

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

发现此时执行了第一个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 }

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

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

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

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

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

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

调用c++函数
进程程序替换+简易版shell实现

调用Python

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

替换函数execle

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

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

进程程序替换+简易版shell实现
如果此时导入自定义变量,发现PATH系统自带的环境变量打印不出,说明添加环境变量给目标进程,是覆盖式的,只要传入自定义的环境变量,那么原来的环境变量,就失去作用了。
进程程序替换+简易版shell实现
进程程序替换+简易版shell实现
如果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,需要循环以下过程:文章来源地址https://www.toymoban.com/news/detail-448697.html

  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 }

到了这里,关于进程程序替换+简易版shell实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Linux初阶】进程替换的应用 - 简易命令行解释器的实现

    🌟hello,各位读者大大们你们好呀🌟 🍭🍭系列专栏:【Linux初阶】 ✒️✒️本篇内容:使用代码手段实现一个简易的命令行解释器,其中功能包括:打印输出提示符、获取用户输入、字符串切割、执行命令、ls指令下拥有颜色提示、cd、echo; 🚢🚢作者简介:计算机海洋的

    2024年02月07日
    浏览(78)
  • 『Linux从入门到精通』第 ⑱ 期 - 学会了程序替换,我决定手写一个简易版shell玩一玩...

    🌸作者简介: 花想云 ,在读本科生一枚,C/C++领域新星创作者,新星计划导师,阿里云专家博主,CSDN内容合伙人…致力于 C/C++、Linux 学习。 🌸 专栏简介:本文收录于 Linux从入门到精通 ,本专栏主要内容为本专栏主要内容为Linux的系统性学习,专为小白打造的文章专栏。

    2024年02月14日
    浏览(45)
  • 【linux】进程替换的应用|shell解释器的实现

    当我们学过了进程替换之后,本篇文章可以根据进程替换的知识带你自主实现一个shell命令行 实现步骤 1.显示命令行提示 2.读取输入指令以及对应选项 3.分割第二步的指令以及选项到命令行参数表中 4.处理内建命令 5.进程替换 我们通过观察bash的命令行提示发现他是由三部分

    2024年04月26日
    浏览(55)
  • 【Linux】Linux进程控制 --- 进程创建、终止、等待、替换、shell派生子进程的理解…

    柴犬: 你好啊,屏幕前的大帅哥or大美女,和我一起享受美好的今天叭😃😃😃 1. 在调用fork函数之后, 当执行的程序代码转移到内核中的fork代码后 ,内核需要分配 新的内存块 和 内核数据结构 给子进程, 内核数据结构包括PCB、mm_struct和页表,然后构建起映射关系 ,同时

    2024年01月16日
    浏览(58)
  • 【Linux】教你用进程替换制作一个简单的Shell解释器

    本章的代码可以访问这里获取。 由于程序代码是一体的,本章在分开讲解各部分的实现时,代码可能有些跳跃,建议在讲解各部分实现后看一下源代码方便理解程序。 我们想要制作一个简单的 Shell 解释器,需要先观察Shell是怎么运行的,根据 Shell 的运行状态我们再去进行模

    2024年02月02日
    浏览(71)
  • 【Linux】进程等待和替换——进程等待的原理、wait/waitpid方法、进程程序替换、进程替换原理、替换函数

    1.1进程等待的概念    进程等待指的是父进程等待子进程退出,以获取子进程的退出返回值,并释放子进程占用的资源。    当子进程先于父进程退出,但父进程没有关注子进程的退出状态时, 子进程会为了保存自己的退出状态而保持资源占用, 这种情况被称为“僵尸进

    2024年02月04日
    浏览(43)
  • Linux进程控制【进程程序替换】

    ✨个人主页: Yohifo 🎉所属专栏: Linux学习之旅 🎊每篇一句: 图片来源 🎃操作环境: CentOS 7.6 阿里云远程服务器 Good judgment comes from experience, and a lot of that comes from bad judgment. 好的判断力来自经验,其中很多来自糟糕的判断力。 子进程 在被创建后,共享的是 父进程 的代码

    2024年01月17日
    浏览(70)
  • [Linux 进程控制(二)] 进程程序替换

    首先,我们要认识到,我们之前fork()所创建的子进程,执行的代码,都是父进程的一部分(用if-else分流或者执行同样的代码)! 如果我们想让子进程执行新的程序呢? 执行全新的代码和访问全新的数据,不再和父进程有瓜葛,这种技术就叫做程序替换 ,下面我们就来学习一

    2024年03月14日
    浏览(50)
  • Linux--进程控制(2)--进程的程序替换(夺舍)

    目录 进程的程序替换 0.相关函数 1.先看现象  2.解释原理 3.将代码改成多进程版  4.使用其它的替换函数,并且认识函数参数的含义 5.其它  关于进程替换我们需要了解的6个函数: 函数解释: 这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。 如果调用出

    2024年04月29日
    浏览(38)
  • Linux :进程的程序替换

    目录 一、什么是程序替换 1.1程序替换的原理 1.2更改为多进程版本 二、各种exe接口 2.2execlp  ​编辑 2.2execv 2.3execle、execve、execvpe 用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种

    2024年04月10日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包