HNU-2024操作系统实验-Lab9-Shell

一、 实验目的

  1. 理解Shell程序的原理、底层逻辑和Shell依赖的数据结构等

  2. 在操作系统内核MiniEuler上实现一个可用的Shell程序

  3. 能够根据相关原理编写一条可用的Shell指令

二、 实验过程

首先从底层出发,实现Shell程序

1.在src/include目录下新建prt_shell.h头文件:

在这里插入图片描述

这个板块中主要定义了shell能够显示的最大长度以及文件路径的最大值,然后定义了ShellCB控制块,用于Shell的管理,其中包含对用户输入、输入命令的历史的管理以及维护当前工作目录等,此处的ShellBuf是作为Shell的缓冲区,输入的指令会存放在缓冲区内,经过解析得到最终应该执行的操作。

2.向src/bsp目录下的print.c文件中的PRT_UartInit 添加初始化代码,使其支持接收数据中断。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

由于我们的操作系统内核MiniEuler不能使用标准输入输出流,所以需要通过串口交互的方式来实现往Shell的缓冲区中写入字符,在此板块中首先定义了一系列串口的配置位和掩码,如:TXE是串口的第9位(从0开始),在实验二中也进行过类似的操作,接着定义了一个用于UART串口接收数据时使用的信号量sem_uart_rx,最后进行串口的初始化,首先禁用UART,清空中断状态,设定中断mask(允许接收中断),并设置波特率相关寄存器(UARTIBRD和UARTFBRD)。

然后读取Line Control Register(LCR)的当前配置,将其与一组掩码进行或操作来设置数据位、奇偶校验和停止位的配置,然后写回寄存器。这里使用的配置是8个数据位、无奇偶校验、1个停止位,并启用FIFO。最后通过设置控制寄存器(UART Control Register)来启用UART,并使能接收和发送。串口配置完成后,调用OsGicIntSetConfig,OsGicIntSetPriority,OsGicClearInt,OsGicEnableInt函数来配置UART接收中断,最后创建数据接收信号量。

3.在src/bsp目录中的print.c文件中实现 OsUartRxHandle()处理接收中断

在这里插入图片描述

该中断处理函数首先读取UART的状态寄存器,然后检查flag中的第4位(接收FIFO空标志)是否为0,为0则表示UART接收到的字符非空,将接收到的字符读入shell的缓冲区,使用Offset表示写入字符在缓冲区中的偏移(用于定位),当偏移超出shell缓冲区的最大长度时,将其重置为0,从而实现循环缓冲区的效果,最后调用PRT_SemPost函数,发送信号量sem_uart_rx,通知其他可能在等待UART接收数据的任务已经读入新的字符。

4.在src/bsp目录下的prt_exc.c文件中修改中断激活函数:

在这里插入图片描述

当接收到的中断处理号为33时,调用接受处理函数

5.在src/kernel/task目录下的prt_task.c文件中加入display函数

在这里插入图片描述

这个函数主要是用于后续Shell的top指令对应的实际操作函数,依次遍历g_runQueue队列,按照优先级打印出所有任务。

6.在src/kernel/tick目录下的prt_tick.c文件中加入display函数

在这里插入图片描述

用于后续Shell的tick指令的实际操作,打印出当前已经进行的时钟中断次数。

7.在src/shell目录下新建shmsg.c文件

OS_SEC_TEXT void ShellTask(uintptr_t param1, uintptr_t param2, uintptr_t param3, uintptr_t param4)
{
    U32 ret;
    char ch;
    char cmd[SHELL_SHOW_MAX_LEN];
    U32 idx;
    ShellCB *shellCB = (ShellCB *)param1;

    while (1) {
        PRT_Printf("\nminiEuler # ");
        idx = 0;
        for(int i = 0; i < SHELL_SHOW_MAX_LEN; i++)
        {
            cmd[i] = 0;
        }

        while (1){
            PRT_SemPend(sem_uart_rx, OS_WAIT_FOREVER);

            // 读取shellCB缓冲区的字符
            ch = shellCB->shellBuf[shellCB->shellBufReadOffset];
            cmd[idx] = ch;
            idx++;
            shellCB->shellBufReadOffset++;
            if(shellCB->shellBufReadOffset == SHELL_SHOW_MAX_LEN)
                shellCB->shellBufReadOffset = 0;

            PRT_Printf("%c", ch); //回显
            if (ch == '\r'){
                // PRT_Printf("\n");
                if(cmd[0]=='t' && cmd[1]=='o' && cmd[2]=='p'){
                    OsDisplayTasksInfo();
                } else if(cmd[0]=='t' && cmd[1]=='i' && cmd[2]=='c' && cmd[3]=='k'){
                    OsDisplayCurTick();
                }
                break;
            }

        }
    }
}

这段代码的功能很简单,首先输出Shell命令行的提示符“minieuler #”,然后根据Shell缓冲区内的字符(即为输入的指令)进行相应的操作,本实验已经实现的有top指令和tick指令,功能实现在前文中已经详细解释,top指令按照优先级打印出队列中的所有任务,tick指令打印出当前已经执行的时钟中断数,新增加的指令会在后文的作业中详细阐述。

OS_SEC_TEXT U32 ShellTaskInit(ShellCB *shellCB)

{

    U32 ret = 0;

    struct TskInitParam param = {0};

    _// task 1_

    _// param.stackAddr = 0;_

    param.taskEntry = (TskEntryFunc)ShellTask;

    param.taskPrio = 9;

    _// param.name = "Test1Task";_

    param.stackSize = 0x1000; _//__固定4096,参见prt_task_init.c的OsMemAllocAlign_

    param.args[0] = (uintptr_t)shellCB;

    TskHandle tskHandle1;

    ret = PRT_TaskCreate(&tskHandle1, &param);

    if (ret) {

        return ret;

    }

    ret = PRT_TaskResume(tskHandle1);

    if (ret) {

        return ret;

    }

}

这个函数主要目的是使用给定的参数初始化并启动一个新的ShellTask任务。

至此,Shell的底层初始化已经全部完成。

三、 测试及分析

在这里插入图片描述

能够正常运行本实验自带的两条Shell指令

四、 Lab9作业

在实现完Shell的底层原理之后,我们还需要在main函数中启动Shell程序:

在这里插入图片描述

引用外部文件中定义的一系列初始化文件,同时定义本实验中的shellCB控制块

在这里插入图片描述

然后调用初始化函数对Shell进行初始化,就可以顺利启动Shell程序了

这里实现了三条额外的指令:第一条是Shell程序中不可或缺的help指令,可以打印出当前所有的可用指令以及指令用途:

在这里插入图片描述

第二条是清屏操作Clear:

在这里插入图片描述

第三条是退出操作exit:

在这里插入图片描述

演示:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

五、心得体会

通过这个实验,我更深入地理解了命令行Shell的工作原理和底层实现,也顺利实现自己写的两条指令;至此,操作系统课程的所有实验落下帷幕,我本人是感慨万分的,从最开始的什么都看不懂、到处找资料、实验代码一看一整天、实验环境一配一整天,到顺利完成所有的实验,实现了一个自己的简单操作系统内核:MiniEuler,还是成就感满满的。很感谢有这个机会能接触到这么底层的实验,让原本遥不可及的操作系统变得咫尺可得,我也同样明白这只是前行的一小步,未来的学习道阻且长。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值