【linux进程控制(三)】进程程序替换--如何自己实现一个bash解释器?

💓博主CSDN主页:杭电码农-NEO💓

⏩专栏分类:Linux从入门到精通⏪

🚚代码仓库:NEO的学习日记🚚

🌹关注我🫵带你学更多操作系统知识
  🔝🔝


在这里插入图片描述

进程程序替换

  • 1. 前言
  • 2. exec系列函数的认识
  • 3. execl系列函数
  • 4. execv系列函数
  • 5. 程序替换的使用场景
  • 6. 自我实现一个bash解释器
  • 7. 内建命令的特殊性
  • 8. 总结以及拓展

1. 前言

本篇文章是进程控制的最后一篇文章
有时我们遇见这种场景:子进程被创建
出来后并不想执行父进程的代码,而是
想去执行其他程序的代码来完成任务,
于是在这种场景下,程序替换显得很重要!

本章重点:

本篇文章着重讲解进程程序替换
的exec系列函数的用法(一共六个),
并且自主实现一个bash解释器.
最后拓展如何使用C调用其他语言的程序


2. exec系列函数的认识

在fork之后如果子进程想要执行一个
全新的程序,就需要用到此系列函数!

在这里插入图片描述

这里一共有六个函数,但是它们都有
一定的规律,并不难记忆!它们都是失败
返回-1首先先介绍一个最简单的函数:
execl

它的参数分别代表:要执行的程序的路径
以及名字,和如何执行此程序

先使用再解释:

int main()    
{    printf("我要进行程序替换了...\n");    int n = execl("/usr/bin/ls","ls","-a","-l",NULL);                                                                                                                   if(n==-1)    {    perror("execl");    }    printf("程序替换完毕!\n");    return 0;    
}    

现象:

在这里插入图片描述
可以发现,在打印完:我要进行程序替换
后,就去执行了ls程序了,并且执行完后
并没有打印"程序替换完成"!

表面execl后,会将当前进程的代码和数据
进行替换,包括还没执行的代码!


3. execl系列函数

首先,execl隶属于exec系列,加上l,
l就是list,相当于要把执行的程序的路径
给列举出来,execl系列中还有
execlp和execle,现在来讲解这两个

在这里插入图片描述

execlp函数解析:

int main()    
{    printf("我要进行程序替换了...\n");    int n = execlp("ls","-l",NULL);                                                                                                                                     if(n==-1)    {    perror("execl");    }    printf("程序替换完毕!\n");    return 0;    
} 

在这里插入图片描述

可以发现,使用execlp函数即使不加上
路径也可以找到程序,并且允许它,但是
这是为什么呢?带上p,p也就是PATH
环境变量,所以系统会去环境变量PATH
中找路劲,若找到就直接执行它!

execle函数解析:

int main()    
{    const char* _env[]={"MY_ENV=666",NULL};    printf("我要进行程序替换了...\n");    int n = execle("/usr/bin/ls","ls","-l",NULL,_env);//自己定义一个环境变量MY_ENV=666传递给要去执行的程序                                                              if(n==-1)    {    perror("execl");    }    printf("程序替换完毕!\n");    return 0;    
}  

execle可以在执行其他程序前,
传入自己定义的环境变量,方便后续
程序的执行!e也就是env的简写


4. execv系列函数

execv系列函数即为将l换成了v,
v就是vector,数组,也就是利用
数组来传参

在这里插入图片描述

execv函数解析:

int main()    {    char* const set[]={"ls","-a","-l",NULL};  printf("我要进行程序替换了...\n");    int n = execv("/usr/bin/ls",set);                                                                             if(n==-1)    {    perror("execl");    }    printf("程序替换完毕!\n");    return 0;    } 

将我们要执行程序的方法用数组存起来
如何再把数组传过去!后面的execvp和
execvpe函数也就很好理解了,加上p无非
就是去环境变量PATH中找路径,加上e也
就是给要去执行的程序传入环境变量,仅此而已!

在这里插入图片描述


5. 程序替换的使用场景

其实一般情况下,程序替换都不是将
自己替换掉,而是创建子进程去替换,
让子进程去干活,而父进程当"监工"

在这种场景下,我们可以很自然的想到
bash解释器的工作原理可能就是创建
子进程去执行任务,而bash父进程本身
就需要获取指令,并传达命令即可!

首先,bash解释器一定是一个while
死循环,因为它会不断给我们打印信息:
当然这个消息你可以自定义处理

在这里插入图片描述

int main()
{while(1){//打印提示信息printf("[kwy@localhost myshell]# ");fflush(stdout);......}return 0;
}

我们先把bash的整体结构分析一下,
然后在一步一步的实现它:

  1. 首先我们需要定义两个数组A和B
    A用来存放用户输入的所有字符串
    B用来存放以空格打散后的字符串

  2. 第二步,获取用户输入的字符串后,
    将字符串以空格为分割打散

  3. 第三步,创建子进程使用exec系列
    函数去执行用户输入的指令
    而bash本身充当监工的角色等待子进程死亡


6. 自我实现一个bash解释器

首先先创建两个数组备用
然后再接收用户的输入

#define NUM 1000
#define SIZE 16
char cmd_line[NUM];//保存完整的命令行字符串
char* my_argv[SIZE];//保存打散后的字符串
if(fgets(cmd_line,sizeof cmd_line,stdin)==NULL)//用fgets将标准输入输入到数组中continue;
cmd_line[strlen(cmd_line)-1] = '\0';//将输入的换行符给清除掉

接下来就是将字符串以空格为分割打散了
在C语言的学习时有strtok函数可以帮助
我们解决这个问题,它的功能如下:

在这里插入图片描述

这里默认大家知道这个函数的用法了
所以我直接将分割字符串的代码写出来:

//命令行字符串解析:以空格为分割打散
my_argv[0]=strtok(cmd_line," ");//提出第一部分
int index=1;
while(my_argv[index++] = strtok(NULL," "));//第二次调用strtok时若还想解析第一次调用的字符串,则传NULL

这段代码写完后,字符串就已经被我们
分割成了几个小字符串了,比如用户输入
“ls -a -l"就转换成了"ls”,“-a”,"-l"了,接下来
只需创建子进程完成任务即可!

//shell运行原理:通过子进程执行命令,父进程等待&&解析命令
//命令行解释器是一个常驻程序
#define NUM 1000
#define SIZE 16
char cmd_line[NUM];//保存完整的命令行字符串
char* my_argv[SIZE];//保存打散后的字符串int main()
{while(1){//打印提示信息printf("[kwy@localhost myshell]# ");fflush(stdout);memset(cmd_line,'\0',sizeof cmd_line);//获取用户的键盘输入if(fgets(cmd_line,sizeof cmd_line,stdin)==NULL)continue;cmd_line[strlen(cmd_line)-1] = '\0';//将输入的换行符给清除掉//命令行字符串解析:以空格为分割打散my_argv[0]=strtok(cmd_line," ");//提出第一部分int index=1;while(my_argv[index++] = strtok(NULL," "));//第二次调用strtok时若还想解析第一次调用的字符串,则传NULL//fork后子进程去完成任务pid_t id=fork();if(id == 0)//子进程{printf("下面的功能让子进程执行\n");//当执行cd等命令时,改变的是子进程的路径,而父进程的路径没变execvp(my_argv[0],my_argv);exit(1);//执行失败就返回1}//父进程的代码,当监工int status = 0;pid_t ret = waitpid(-1,&status,0);if(ret>0) printf("exit code: %d\n",WEXITSTATUS(status));}return 0;
}

关于代码的解释都在注释中
如果你还有哪个地方不懂,欢迎私信


7. 内建命令的特殊性

在实现bash时,可能会遇见一个问题:
就是cd指令进入某个文件夹似乎没用

这一点其实很好理解,因为指令cd是
进入某个文件夹,而进入此文件夹当然
是当前进程进入了,如果创建了子进程
去进去文件夹,由于写时拷贝的原因,父
进程并不会进去,所以对于像cd这样的
指令我们称为内建命令,也就是不能让
子进程来完成的命令,只能父进程亲自动手!

if(strcmp(my_argv[0],"cd")==0)
{if(my_argv[1]!=NULL)chdir(my_argv[1]);continue;//直接跳到while(1)处
}

chdir即为切换当前的工作目录

内建命令不止cd,像export,kill
和history等等也是内建命令!


8. 总结以及拓展

进程程序替换可以帮助我们完成很多
任务,制作一个简易的bash解释器只是
众多应用中的一个,随着我们学习的深入
你还会发现新大陆!

对于程序替换的拓展:

在Linux下,C语言程序不仅可以替换成
C语言程序去执行,还可以替换成python
或Java甚至是bash等程序去执行它们
语言的代码:

比如python脚本:

#! /usr/bin/python3.6
print("hello Python/n")

运行命令: python test.py

将进程替换为Python程序:execlp("python", "python", "test.py", NULL);

这样就可以直接在C程序上执行python代码了!


🔎 下期预告:基础IO 🔍

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

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

相关文章

maven 下载和安装

点击进入Maven下载网址 Maven – Download Apache Maven Maven详细下载列表 Index of /dist/maven/maven-3 本地仓库配置文件 配置环境变量 idea编辑器配置 maven Javajdk配置 字节码版本是否11

Hive3 on Spark3配置

1、软件环境 1.1 大数据组件环境 大数据组件版本Hive3.1.2Sparkspark-3.0.0-bin-hadoop3.2 1.2 操作系统环境 OS版本MacOSMonterey 12.1Linux - CentOS7.6 2、大数据组件搭建 2.1 Hive环境搭建 1)Hive on Spark说明 Hive引擎包括:默认 mr、spark、…

利用uni-app 开发的iOS app 发布到App Store全流程

1.0.3 20200927 更新官方对应用审核流程的状态。 注:最新审核后续将同步社区另一篇记录 AppStore 审核被拒原因记录及解决措施 :苹果开发上架常见问题 | appuploader使用教程 1.0.2 20200925 新增首次驳回拒绝邮件解决措施。 1.0.1 20200922 首次…

前端实现界面切换主题

✨ 目录 ▷ 样式切换主题▷ 变量设置主题 ▷ 样式切换主题 常用的主题切换实现方式之一,就是通过 link 标签的 rel 属性来实现的当 rel 标签的值是 alternate,就代表该样式是可以替换的title 属性要加就全加上或者全不加,因为 title 会导致系…

SM8081是一个高效率的1.5MHz同步步进降压DC/DC调节器,可提供高达1A的电压输出电流。

SM8081 高效率,1.5MHZ,1A 同步降压调节器 概述: SM8081是一个高效率的1.5MHz同步步进降压DC/DC调节器,可提供高达1A的电压输出电流。它可以在宽输入电压下工作范围从2.5V到5.5V,集成主开关以及具有非常低Rps&#x…

python调用chrome实现网页自动操作

一. 内容简介 python调用chrome实现网页自动操作。 二. 软件环境 2.1vsCode 2.2Anaconda version: conda 22.9.0 2.3代码 链接: 三.主要流程 3.1 下载驱动和插件 调用谷歌浏览器,需要下载浏览器驱动(https://registry.npmmirror.co…

AI毕业设计生成器(基于AI大模型技术开发)

这是一个辅助生成计算机毕业设计的工具,可以自动完成毕业设计的源码。它基于几百个github上面开源的java和python项目,运用tengsorflow技术,训练出了AI大模型。基本实现了计算机毕业设计生成器,能够初步生成Java或python基本源码。…

由浅入深学习统计学 - 常用统计图形学习

学习笔记 第一章- 信息图形化 图形化(可视化) 在一堆数据中,自己发现了这些数据的规律,但是无法表述给其他人知道,图形化就是便于他人理解数据的规律的展示的手段。 或者说我们也可以从统计的数据图形中发现某些没有…

Final Cut Pro X for Mac:打造专业级视频剪辑的终极利器

随着数字媒体技术的不断发展,视频剪辑已经成为各行各业不可或缺的一部分。Final Cut Pro X for Mac作为一款专业的视频剪辑软件,凭借其强大的功能和易用性,已经成为Mac用户的首选。本文将向您详细介绍Final Cut Pro X for Mac的优势、功能以及…

SOME/IP 协议介绍(四)RPC协议规范

RPC协议规范 本章描述了SOME/IP的RPC协议。 传输协议绑定 为了传输不同传输协议的SOME/IP消息,可以使用多种传输协议。SOME/IP目前支持UDP和TCP。它们的绑定在以下章节中进行了解释,而第[SIP_RPC_450页,第36页]节讨论了选择哪种传输协议。…

阿里云服务器白嫖还是选99元特价主机?

阿里云服务器0元到手攻略,咱别0元了,花点钱吧,99元行吗?不行也有0元的,一会阿腾云atengyun.com给大家细说。先说99元即可购买一台阿里云服务器,2核2G配置、3M固定带宽、40G系统盘,第二年续费还是…

Zabbix SNMPv3

一、Snmpv3简述 SNMPv3是Simple Network Management Protocol version 3(简单网络管理协议第三版)的缩写。它是一种网络管理协议,用于监控和管理网络中的设备、系统和应用程序。 相对于之前的版本,SNMPv3具有更强的安全性和扩展…