实现一个命令行解释器

1、初始化命令行解释器

在计算机系统中,操作系统内核是一个很大得到软件模块,用户不能直接去使用。因此计算机提供一个外壳shell,用户能够通过这个外壳执行各种应用程序,从而完成操作系统各种功能。

shell:命令解释器。允许用户交互式的输入命令并解释执行,并且可以调用出相应的应用程序运行,从而让用户能够使用内核的功能去操作计算机

如mac中的bash ,window中的cmd Linux中的shell

目标:设计一个命令行的shell应用程序,解释用户输入的命令并解释执行。

shell应用程序,内置一个命令表,包含可以被执行的若干指令。当命令不存在于命令表中,尝试从磁盘加载应用程序

2、实现help命令

以help命令,演示如何在shell中执行内置命令

如何解析命令字符串,按照int argv, char argv的方式

1)通过遍历内置命令表:command list 

2)help命令的实现包括获取用户输入,解析输入缓冲区中的字符,以及执行相应的命令

具体过程:

使用strchr函数查找特定字符位置,并将其设置为0,以清理输入字符

定义strtok函数,用于分割字符串、提取命令和参数

ARGV和ARGC用于传递参数和参数数量给命令执行函数

命令的查找与执行

通过查找命令表,找到用户输入的命令对应的函数指针

如果命令是内置命令,直接执行相应函数

如果命令在磁盘上,需要从磁盘加载并执行相应的子应用程序

命令的输入与解析

使用特定长度的输入函数fgets获取标准输入,避免数据量过大的问题

解析输入字符串,取出不必要的回车和换行字符,以便后续处理

利用strtok函数按照空格分割输出字符串,提取命令和参数

将提取出的参数字符串转为指针数组,传递给相应的内置命令执行函数

3、执行echo命令并给输出加颜色

加了信息输出时使用不同颜色的功能,以及演示了如何在程序中解析命令行的参数选项

显示颜色和清屏

两项功能是通过发送ESC转义序列完成,根本实现颜色的控制和清屏,只需要发送相应的转义序列。

命令选项的解析

使用getopt函数(Newlib提供)进行命令行的参数解析。

int getopt(int argc, char * const argv[],  const char *optstring); 

使用echo命令时,使用的形式为echo -n 重复数量 -h 待重复的字符串,其中有-n和-h为选项,且-n的后边必须加上一个数量值。

因此在调用getopt时,optstring需要设置为n:h(n后面的:表示n必须有几个参数值)

while ((ch = getopt(argc, argv, "n:h")) != -1) {

}

在设置optstring后,每次调用时,getopt函数会逐个扫描argv相应的选项,如n或者h

如果返回的是期望的选项,如n或者h,我们可以从全局变量optarg中获取对应的值

如果返回的是?,则表示解析出的选项不可识别,例如echo -x,此时optopt的值将会被设置成x

当返回时-1时,表示已经没有参数,即对于echo -n 重复数量 -h 字符串,表示已经扫描完所有以-开头的选项,后续已经没有其它选项。

while ((ch = getopt(argc, argv, "n:h")) != -1) {
     switch (ch) {
         case 'h':
             puts("echo echo any message");
             puts("Usage: echo [-n count] msg");
             optind = 1;        // getopt需要多次调用,需要重置
             return 0;
         case 'n':
             count = atoi(optarg);
             break;
         case '?':
             if (optarg) {
                 fprintf(stderr, "Unknown option: -%s\n", optarg);
             }
             optind = 1;        // getopt需要多次调用,需要重置
             return -1;
     }
 }

getopt被调用时,getopt会调整argv中各参数的顺序。因此当getopt优先返回-1时,optind指向了第一个非选项的参数的索引,即待重复的字符串在argv中索引号。因此,我们此时可以通过argv[optind]来获取待重复的字符串。

由于getopt将会在其它命令的执行时重复使用,因此需要在退出echo命令的执行时,将optind重置。因为optind 是 argv 中要处理的下一个元素的索引,该变量为全局变量,getopt会依赖此值。

3、为进程增加exit接口

在应用程序的执行过程中,程序可能会主动结束执行,或者从main返回,因此需要给进程一个主动退出执行的接口。应用程序通常以main函数开头,通过return语句退出函数,但也可以使用EXIT库函数退出。

exit系统调用的功能是允许进程在任意位置主动退出,结束执行,任何属于该进程的打开的文件描述符都会被关闭。因为exit已经有newlib库实现,我们只需要实现更底层的exit

exit系统调用接口实现:

  • 添加系统调用表配置项
  • sysy_exit系统调用的实现应停止进程执行,回收相关资源,内存
  • 进程退出前会关闭打开的文件,将状态信息保存到进程的task结构中
  • 应用程序返回时,应直接调用exit函数终止进程,且exit函数应包含应用程序的返回操作。

4、为进程增加wait接口

在shell加载程序运行时,需要程序的执行结果,因此需要调用wait来获取子程序的执行结果

int wait(int* status);

wait系统调用的功能:

  1. 回收子进程的所有资源
  2. 获取子进程的运行结果,如果调用的子进程还未结束,则会等待

僵尸进程(并未进行处理)

如果父进程没有主动调用wait来回收子进程的资源,则子进程会成为僵尸进程。

僵尸进程是当子进程比父进程先结束,而父进程又没有回收子进程,释放子进程占用的资源,此时子进程将成为一个僵尸进程。如果父进程先退出 ,子进程被init接管,子进程退出后init会回收其占用的相关资源

5、让子进程继承父进程已打开的文件

考虑到fork的功能是创建一个完全相同的子进程,因此需要将增加的打开文件进行复制。如果直接内存复制在进程间是不可行的,因为两个进程的状态是独立的

文件表的复制与引用计数

  • 文件表的复制通过遍历父进程的文件表并复制其内容
  • 引用计数用于跟踪文件的打开次数,确保多个进程访问时文件不会被意外关闭
  • 子进程创建时,需要增加被打开的文件的引用计数。

进程间文件访问冲突解决

  • 多个进程同时往控制台写数据时,需要解决写冲突。
  • 通过在写操作前加锁,确保一次只允许一个进程写入。
  • 锁机制保护了控制台写操作,避免了多个进程同时写入的冲突

  • 13
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值