小工具知识网

当前位置: 首页 > 行业知识 > 【linux进程控制(三)】进程程序替换--如何自己实现一个bash解释器?

行业知识

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

2026-10-25 02:02:43 来源:互联网转载

博主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 16char cmd_line[NUM];//保存完整的命令行字符串char* my_argv[SIZE];//保存打散后的字符串if(fgets(cmd_line,sizeof cmd_line,stdin)==NULL)//用fgets将标准输入输入到数组中    continue;cmd_line[strlen(cmd_line)-1] = '
    //命令行字符串解析:以空格为分割打散my_argv[0]=strtok(cmd_line," ");//提出第一部分int index=1;while(my_argv[index++] = strtok(NULL," "));//第二次调用strtok时若还想解析第一次调用的字符串,则传NULL
    ';//将输入的换行符给清除掉

    接下来就是将字符串以空格为分割打散了

    在C语言的学习时有strtok函数可以帮助

    我们解决这个问题,它的功能如下:

    这里默认大家知道这个函数的用法了

    所以我直接将分割字符串的代码写出来:

    //shell运行原理:通过子进程执行命令,父进程等待&&解析命令//命令行解释器是一个常驻程序#define NUM 1000#define SIZE 16char cmd_line[NUM];//保存完整的命令行字符串char* my_argv[SIZE];//保存打散后的字符串int main(){    while(1)    {        //打印提示信息        printf("[kwy@localhost myshell]# ");        fflush(stdout);        memset(cmd_line,'

    7. 内建命令的特殊性

    ',sizeof cmd_line); //获取用户的键盘输入 if(fgets(cmd_line,sizeof cmd_line,stdin)==NULL) continue; cmd_line[strlen(cmd_line)-1] = '
    if(strcmp(my_argv[0],"cd")==0){   if(my_argv[1]!=NULL)       chdir(my_argv[1]);   continue;//直接跳到while(1)处}
    ';//将输入的换行符给清除掉 //命令行字符串解析:以空格为分割打散 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;}

    这段代码写完后,字符串就已经被我们

    分割成了几个小字符串了,比如用户输入

    “ls -a -l"就转换成了"ls”,“-a”,"-l"了,接下来

    只需创建子进程完成任务即可!

    8. 总结以及拓展

    关于代码的解释都在注释中

    如果你还有哪个地方不懂,欢迎私信


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

    在实现bash时,可能会遇见一个问题:

    就是cd指令进入某个文件夹似乎没用

    这一点其实很好理解,因为指令cd是

    进入某个文件夹,而进入此文件夹当然

    是当前进程进入了,如果创建了子进程

    去进去文件夹,由于写时拷贝的原因,父

    进程并不会进去,所以对于像cd这样的

    指令我们称为内建命令,也就是不能让

    子进程来完成的命令,只能父进程亲自动手!

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

    内建命令不止cd,像export,kill

    和history等等也是内建命令!


    进程程序替换可以帮助我们完成很多

    任务,制作一个简易的bash解释器只是

    众多应用中的一个,随着我们学习的深入

    你还会发现新大陆!

    对于程序替换的拓展:

    在Linux下,C语言程序不仅可以替换成

    C语言程序去执行,还可以替换成python

    或Java甚至是bash等程序去执行它们

    语言的代码:

    比如python脚本:

    运行命令: python test.py

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

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


    下期预告:基础IO

上一篇:武汉电信机房在哪里买?

下一篇:怎么帮自己的孩子异地备案