【linux系统编程】剖析shell运行原理

深入理解shell工作原理

1. 简单介绍shell:

Windows以图形化界面为主要交互方式,简单高效,易于日常使用。Linux以命令行界面为主要交互方式,操作相较windows,mac来说比较困难一些,但linux也有向着图形化界面努力的桌面系统,例如ubuntu。Windows和Linux的交互方式虽然不同,但其本质上却是一样,图形化界面和命令行界面都是为了让用户进行相关操作,而图形化界面和命令行界面就是我们所说的 " 外壳程序 "。
在这里插入图片描述

Linux严格意义上来说是一个操作系统,我们称之为 "核心 (kernel) ",但我们一般用户不能直接使用kernel,而是通过kernel的 “外壳程序”,也就是所谓的Shell,来与 kernel 沟通。

我们常把shell描述为 " 命令行解释器 " :
Shell只是所有外壳程序的统称,例如在centos 7当中默认的外壳程序名叫bash
在这里插入图片描述
而实质上shell的种类是很多的,以centos 7 为例:
在这里插入图片描述

  • shell 将使用者的命令翻译给核心 (kernel) 处理
  • shell 将核心的处理结果翻译给使用者

对于Windows中的图形化界面(GUI),使用Windows并不是直接操作Windows内核,而是通过图形接口,点击,从而完成我们的操作。Shell对于Linux具有相同的作用,主要是对我们发出的指令进行解析,解析指令交给Linux内核,内核进行运算运行出结果,再通过Shell呈现给用户

2. shell工作原理:

Linux系统提供给用户的最重要的系统程序是Shell命令语言解释程序。它不属于内核部分,而是在核心之外,以用户态方式运行。其基本功能是解释并执行用户敲入的各种命令,实现用户与Linux kernel 的接口。linux系统启动后,核心为每个终端用户建立一个进程去执行Shell解释程序。它的执行过程基本上按如下步骤:

  • ① 读取用户由键盘输入(stdin)的命令行指令
  • ② 分析命令,以命令名作为文件名,并将其它参数处理为系统调用execve( )内部处理所要求的形式
  • ③ 终端进程调用fork( )创建一个子进程来代替执行命令行
  • ④ 终端进程本身用系统调用wait_pid( )来等待子进程执行结束(如果是后台命令,则不等待);当子进程运行时调用execve( ),子进程根据文件名(即命令名)到目录中查找有关文件(这是命令解释程序构成的文件),将它调入内存,执行这个程序(解释这条命令)
  • ⑤ 如果命令末尾有&号(后台命令符号),则终端进程不用系统调用wait_pid( )等待,立即让用户输入下一个命令,转到①步骤;如果命令末尾没有&号,则终端进程要一直等待,当子进程(即运行命令的进程)完成处理后终止,向父进程(终端进程)发送退出信息(退出码和终止信号),此时父进程被唤醒,在做必要的判别等工作后,终端进程发提示符,让用户输入新的命令,转到①重复上述处理过程

这里注意:上述步骤中提到的execve()是系统调用,linux中有一个exec族,包含六个exec函数,这里具体调用哪一个函数看它的具体实现~~

简单一点概括就是:

  • ① fork创建子进程,让子进程进行命令行解释
  • ② 子进程出现任何问题,都不影响父进程Shell,进程间具有相对独立性
  • ③ 我们所使用的shell在执行完命令行后一直不会退出,本质上就是一直在fork子进程,然后让子进程代替父进程执行命令,子进程结束后向父进程发送退出信息,父进程处理退出信息 (防止子进程变为僵尸进程),循环执行以上操作,这种机制在一定程度上保证了shell 的安全

对比到Windows当中就是,我们每运行一个程序就相当于创建了一个子进程,例如,登录微信,QQ,网易云;而这些子进程当中任何一个进程出现问题,都不会影响父进程,例如,当你的QQ出现卡死情况(程序异常)或你的QQ被关掉(程序终止),但其他子程序仍然可以运行,同时,我们依然可以通过双击的方式启动其他进程,也就是说,各种操作系统本质上让我们能直接使用的都是外壳程序,这种外壳程序在linux中叫做shell,我们在这层外壳上所做的任何操作都不会影响kernel,子进程的退出也不会影响到shell本身,这称为shell的安全机制。
在这里插入图片描述
例如我把QQ进程结束掉之后并不会影响Chrome进程,更不会对shell的后续操作产生影响,本质上进程间是相互独立的 (父子进程代码共享,数据各自私有)

3. 简单shell实现:

#include <stdio.h>
#include <stdlib.h> 
#include <sys/wait.h>
#include <string.h>
#include <unistd.h>
#include <iostream>

#define SIZE 1024
#define NUM 64
using namespace std;

int main()
{
   while (true)
   {
     //捕捉键盘输入
     printf("[iwannabe@localhost pwd]$ ");
     //fflush(stdout),对标准输出流的清理,但是它并不是把数据丢掉。
     //而是及时地打印数据到屏幕上。
     //标准输出是以行为单位进行的,也即碰到\n才打印数据到屏幕。
     //这就可能造成延时。
     fflush(stdout);
     char buf[SIZE] = {0};
     fgets(buf, SIZE - 1, stdin);
     buf[strlen(buf) - 1] = '\0';//把最后的\n置为'\0'
     
      //解析键盘输入
      int myargc = 0;
      char* ptr = buf;
      char* myargv[NUM] = {NULL};
      while(*ptr != '\0')
      {
        if (*ptr != ' ')
        {
          myargv[myargc] = ptr;
          myargc++;
          while(*ptr != '\0'&&*ptr != ' ')
          {
            ptr++;
          }
         *ptr = '\0';
       }
        ptr++;
      }
      myargv[myargc] = NULL;
      if(strcmp("cd",myargv[0]) == 0)
      {
       chdir(myargv[1]);
       continue;
      }
      
      //创建子进程
      pid_t pid = fork();
      if(pid<0)
      {
        perror("fork error");
        continue;
      }
      else if (pid == 0)
      {
      //子进程执行对于命令名称的程序(程序替换)
        execvp(myargv[0], myargv);
        perror("execvp error");
        exit(-1);
      }
       //父进程等待
        wait(NULL);
    }
    return 0;
}

在这里插入图片描述
vim:
在这里插入图片描述
运行结果:
在这里插入图片描述

  • 15
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 10
    评论
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宁海没有七号公园

谢谢你%%%

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值