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

news2024/10/5 13:34:09

💓博主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.coloradmin.cn/o/1201641.html

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

相关文章

固定主机1500PLC与两台移动1200PLC之间以太网通讯

本方案搭建的是固定主机1500PLC与两台移动1200PLC之间以太网通讯。 无线通讯网络搭建 首先在固定端主机设备上的西门子S7-1500PLC上搭载一块达泰DTD418MB作为主站。然后在两台移动的西门子S7-1200PLC上分别搭载一块达泰DTD418MB作为从站。由此,便通过DTD418MB搭建…

线程的互斥

目录 线程互斥 线程互斥的背景知识 多线程抢票 多线程抢票加锁 锁的原理 可重入函数与线程安全 常见的线程安全的情况 常见的不可重入情况 常见的可重入情况 总结 线程互斥 线程互斥的背景知识 临界资源:临界资源就是多个执行流共享的资源就叫做临界资源…

勘察设计考试公共基础之物理篇

2、物理 理想气体的压强P23nw (n为分子的密度数),w12mv2 (m为分子质量),v2 为分子速率平方的平均值。PnKT,∴w32KT 即:理想气体分子的平均平动动能与气体的温度成正比,气…

【商城更新】神秘市场通行证上架、齿轮头归来//及下架内容

本周商城将于11月8号更新。本次商城除了神秘市场2023通行证上架之外,还有齿轮头黑货箱也会上架藏匿处。随之小兔奇趣齐聚大礼包、危险玩偶大礼包等饰品下架商城。 上架饰品: ▲神秘市场2023通行证 神秘市场2023通行证基础版 售价:1200G-coi…

Xilinx Artix7-100T低端FPGA解码MIPI视频,基于MIPI CSI-2 RX Subsystem架构实现,提供工程源码和技术支持

目录 1、前言免责声明 2、我这里已有的 MIPI 编解码方案3、本 MIPI CSI2 模块性能及其优缺点4、详细设计方案设计原理框图OV5640及其配置权电阻硬件方案MIPI CSI-2 RX SubsystemSensor Demosaic图像格式转换Gammer LUT伽马校正VDMA图像缓存AXI4-Stream toVideo OutHDMI输出 5、…

实验一 Anaconda安装和使用(Python程序设计实验报告)

实验一 Anaconda安装和使用 一、实验环境 Python集成开发环境IDLE/Anaconda 二、实验目的 1.掌握Windows下Anaconda的安装和配置。 2. 掌握Windows下Anaconda的简单使用,包括IDLE、Jupyter Notebook、Spyder工具的使用。 3. 掌握使用pip管理Python扩展库…

中国专利转让数据集(1985-2021年)

专利转让数据追踪和记录专利从一个实体转移到另一个实体的过程。这些数据不仅包括参与转让的申请人和受让人的身份信息,如名字和地址,还涵盖了转让的具体法律细节,包括转让执行日、转让次数、法律状态变更,以及转让登记的相关信息…

FL Studio最新版本号21.2发行更新啦

Image Line宣布发布FL Studio 21.2。更新带来了许多改进,但主要功能是引入了新的词干分离功能和FL Cloud,这是一个新的在线平台,直接与DAW集成,为用户提供从循环和样本到母带和发行功能的一切。 词干分离与FL云 随着最新更新的发…

AI生成PPT工具——Gamma,结合GPT生成不错的效果

AI生成PPT工具——Gamma,结合GPT生成不错的效果 先告诉GPT我现在要参加一个比赛,请他帮忙梳理一下内容。当然整个过程需要不断调整,GPT生成的内容也不是一次就是最好的 不断调整之后让其列出提纲即可,如下: 紧接着我们…

谈谈前端如何防止数据泄露

shigen日更文章的博客写手,擅长Java、python、vue、shell等编程语言和各种应用程序、脚本的开发。记录成长,分享认知,留住感动。 最近突然发现了一个好玩的事情,部分网站进去的时候几乎都是死的,那种死是区别于我们常见…

【Hadoop】MapReduce详解

🦄 个人主页——🎐开着拖拉机回家_大数据运维-CSDN博客 🎐✨🍁 🪁🍁🪁🍁🪁🍁🪁🍁 🪁🍁🪁&#x1f…

讨论编程建议,生成需求文档:SolidGPT 对话代码,智能互动 | 开源日报 No.73

vuejs/vue Stars: 205.6k License: MIT Vue 是一个用于构建用户界面的渐进式框架。它从头开始设计,可以根据不同的使用情况轻松地在库和框架之间进行扩展。Vue 由一个专注于视图层的核心库组成,并且还有一系列支持性库来帮助您处理大型单页应用程序中的…

哈夫曼树(定义,构造,哈夫曼编码)

目录 1.带权路径长度2.哈夫曼树的定义3.哈夫曼树的构造1.哈夫曼树的特性 4.哈夫曼编码1.编码方式2.应用 1.带权路径长度 ①结点的权:有某种现实含义的数值(如:表示结点的重要性等) ②结点的带权路径长度:从树的根到该结点的路径长度(经过的边数&#xff…

光明源@智慧公厕是如何提升城市人们生活质量的?

随着城市人口不断增加,城市生活质量成为社会关注的焦点。在城市规划中,智慧公厕作为一项创新的基础设施,正日益受到重视。它们不仅提供卫生便捷的服务,还通过科技的运用,显著提升了城市居民的生活质量。本文将深入探讨…

C++进阶篇4---番外-红黑树

一、红黑树的概念 红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或 Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路 径会比其他路径长出俩倍&#xff0…

Springboot项目部署及多环境开发

一、项目部署 我们之前写的代码都是部署在本地的tomcat上,别人是无法访问我们写的程序的。在实际开发中,我们都要将开发完毕的项目部署到公司的服务器上。 我们的代码需要经过编译打包生成一个jar包,这个过程需要借助一个插件来实现。 创建sp…

Linux常用命令——bzip2recover命令

在线Linux命令查询工具 bzip2recover 恢复被破坏的.bz2压缩包中的文件 补充说明 bzip2recover命令可用于恢复被破坏的“.bz2”压缩包中的文件。 bzip2是以区块的方式来压缩文件,每个区块视为独立的单位。因此,当某一区块损坏时,便可利用b…

AIOT数字孪生智慧工地一体化管理平台源码

智慧工地app基于物联网和移动互联网技术,利用各类传感器及终端设备通过与云端服务器的实时数据交互,为施工现场的管理人员提供环境监测、劳务实名制管理、物料管理、巡检记录、设备管理等一系列优质高效的行业解决方案。 一、智能工地应用价值 智慧工地…

有效找回误删照片的 6 种照片数据恢复软件!

照片是珍惜过去珍贵时刻的唯一方式。它们让记忆永存,帮助我们重温生命中最美好的时刻。但是,当这些时刻丢失时会发生什么?您是否曾经因系统崩溃而意外删除或丢失照片?丢失照片可能令人心碎,但仍有希望,因为…

SOLIDWORKS实用技巧之焊件轮廓应用

1.焊件轮廓库官方下载入口 焊件轮廓可以自制,也可以从软件中在线下载获取直接使用,如图1,联网状态按ctrl左键点击下载,解压后获得库文件。 图1 图2 2.库放置的位置和配置 从SOLIDWORKS2014版起,软件焊件轮廓库支持可…