首页 > 其他分享 >HNU-操作系统实验lab9-2022级

HNU-操作系统实验lab9-2022级

时间:2024-11-02 20:16:24浏览次数:5  
标签:shellCB UART void cmd PRT HNU 2022 Printf lab9

实验过程

新建 src/include/prt_shell.h 头文件:

#ifndef _HWLITEOS_SHELL_H
#define _HWLITEOS_SHELL_H

#include "prt_typedef.h"

#define SHELL_SHOW_MAX_LEN    272
#define PATH_MAX        1024

typedef struct {
    U32   consoleID;
    U32   shellTaskHandle;
    U32   shellEntryHandle;
    void     *cmdKeyLink;
    void     *cmdHistoryKeyLink;
    void     *cmdMaskKeyLink;
    U32   shellBufOffset;
    U32   shellBufReadOffset;
    U32   shellKeyType;
    char     shellBuf[SHELL_SHOW_MAX_LEN];
    char     shellWorkingDirectory[PATH_MAX];
} ShellCB;

#endif /* _HWLITEOS_SHELL_H */

该结构体包含多个成员,负责跟踪 shell 的各种状态信息,包括控制台 ID、任务句柄、命令历史记录等。

consoleID:用于标识控制台。

shellTaskHandle:保存 shell 任务的句柄。

shellEntryHandle:保存 shell 条目的句柄。

cmdKeyLink:指向命令键链的指针,可能是链表或数组的某种数据结构。

cmdHistoryKeyLink:指向命令历史键链的指针。

cmdMaskKeyLink:指向命令屏蔽键链的指针。

shellBufOffset:shell 缓冲区的偏移量,表示当前写入位置。

shellBufReadOffset:shell 缓冲区的读取偏移量。

shellKeyType:保存键类型,可能用于命令解析。

shellBuf:用于存储 shell 输入的字符缓冲区。

shellWorkingDirectory:存储 shell 的当前工作目录。

接收输入

QEMU的virt机器默认没有键盘作为输入设备,但当我们执行QEMU使用 -nographic 参数(disable graphical output and redirect serial I/Os to console)时QEMU会将串口重定向到控制台,因此我们可以使用UART作为输入设备。

在 src/bsp/print.c 中的 PRT_UartInit 添加初始化代码,使其支持接收数据中断。 同时定义了用于串口接收的信号量 sem_uart_rx。

#include "prt_sem.h"
#include "prt_shell.h"


#define UARTCR_UARTEN (1 << 0)
#define UARTCR_TXE (1 << 8)
#define UARTCR_RXE (1 << 9)

#define UARTICR_ALL (1 << 0)

#define UARTIMSC_RXIM (1 << 4)

#define UARTIBRD_IBRD_MASK 0xFFFF
#define UARTFBRD_FBRD_MASK 0x3F

#define UARTLCR_H_WLEN_MASK (3 << 5)
#define UARTLCR_H_PEN (1 << 1)
#define UARTLCR_H_STP1 (0 << 3)

SemHandle sem_uart_rx;

extern void OsGicIntSetConfig(uint32_t interrupt, uint32_t config);
extern void OsGicIntSetPriority(uint32_t interrupt, uint32_t priority);
extern void OsGicEnableInt(U32 intId);
extern void OsGicClearInt(uint32_t interrupt);
extern U32 PRT_Printf(const char *format, ...);
U32 PRT_UartInit(void)
{
    U32 result = 0;
    U32 reg_base = UART_0_REG_BASE;

    UART_REG_WRITE(0, (unsigned long)(reg_base + 0x30));// 禁用pl011
    UART_REG_WRITE(0x7ff, (unsigned long)(reg_base + 0x44));// 清空中断状态
    UART_REG_WRITE(UARTIMSC_RXIM, (unsigned long)(reg_base + 0x38));// 设定中断mask,需要使能的中断
    UART_REG_WRITE(13, (unsigned long)(reg_base + 0x24));
    UART_REG_WRITE(1, (unsigned long)(reg_base + 0x28));

    // https://developer.arm.com/documentation/ddi0183/g/programmers-model/register-descriptions/line-control-register--uartlcr-h?lang=en
    result = UART_REG_READ((unsigned long)(reg_base + DW_UART_LCR_HR));
    result = result | UARTLCR_H_WLEN_MASK | UARTLCR_H_PEN | UARTLCR_H_STP1 | DW_FIFO_ENABLE;
    UART_REG_WRITE(result, (unsigned long)(reg_base + DW_UART_LCR_HR)); // 8N1 FIFO enable

    UART_REG_WRITE(UARTCR_UARTEN | UARTCR_RXE | UARTCR_TXE, (unsigned long)(reg_base + 0x30));// 启用pl011


    // 启用UART 接收中断
    OsGicIntSetConfig(33, 0); //可省略
    OsGicIntSetPriority(33, 0);
    OsGicClearInt(33); //可省略
    OsGicEnableInt(33);

    // 创建uart数据接收信号量
    U32 ret;
    ret = PRT_SemCreate(0, &sem_uart_rx);
    if (ret != OS_OK) {
        PRT_Printf("failed to create uart_rx sem\n");
        return 1;
    }

    return OS_OK;
}

这个函数的主要功能是初始化UART,包括禁用UART、清空中断状态、设置波特率、配置行控制寄存器、启用UART、配置GIC中断控制器和创建UART数据接收信号量。通过这些步骤,确保UART可以正常工作并处理接收中断。

  1. 禁用pl011 UART:
    • UART_REG_WRITE(0, (unsigned long)(reg_base + 0x30));
    • 禁用UART,确保在配置过程中不会有数据传输。
  2. 清空中断状态:
    • UART_REG_WRITE(0x7ff, (unsigned long)(reg_base + 0x44));
    • 清除所有中断状态,确保没有残留的中断。
  3. 设定中断mask:
    • UART_REG_WRITE(UARTIMSC_RXIM, (unsigned long)(reg_base + 0x38));
    • 使能接收中断。
  4. 设置波特率:
    • UART_REG_WRITE(13, (unsigned long)(reg_base + 0x24));
    • UART_REG_WRITE(1, (unsigned long)(reg_base + 0x28));
    • 设置波特率的整数和小数部分。
  5. 配置行控制寄存器:
    • result = UART_REG_READ((unsigned long)(reg_base + DW_UART_LCR_HR));
    • result = result | UARTLCR_H_WLEN_MASK | UARTLCR_H_PEN | UARTLCR_H_STP1 | DW_FIFO_ENABLE;
    • UART_REG_WRITE(result, (unsigned long)(reg_base + DW_UART_LCR_HR));
    • 配置UART为8位数据,无奇偶校验,1个停止位,启用FIFO。
  6. 启用pl011 UART:
    • UART_REG_WRITE(UARTCR_UARTEN | UARTCR_RXE | UARTCR_TXE, (unsigned long)(reg_base + 0x30));
    • 启用UART的发送和接收功能。
  7. 配置GIC中断控制器:
    • OsGicIntSetConfig(33, 0);
    • OsGicIntSetPriority(33, 0);
    • OsGicClearInt(33);
    • OsGicEnableInt(33);
    • 配置和启用UART接收中断。
  8. 创建UART数据接收信号量:
    • ret = PRT_SemCreate(0, &sem_uart_rx);
    • 创建一个初始值为0的信号量,用于UART数据接收。
  9. 返回结果:
    • return OS_OK;
    • 初始化成功返回 OS_OK,否则返回错误码。

在 src/bsp/print.c 中实现 OsUartRxHandle() 处理接收中断:

extern ShellCB g_shellCB;
void OsUartRxHandle(void)
{
    U32 flag = 0;
    U32 result = 0;
    U32 reg_base = UART_0_REG_BASE;

    flag = UART_REG_READ((unsigned long)(reg_base + 0x18));
    while((flag & (1<<4)) == 0)
    {
        result = UART_REG_READ((unsigned long)(reg_base + 0x0));
        // PRT_Printf("%c", result);

        // 将收到的字符存到g_shellCB的缓冲区
        g_shellCB.shellBuf[g_shellCB.shellBufOffset] = (char) result;
        g_shellCB.shellBufOffset++;
        if (g_shellCB.shellBufOffset == SHELL_SHOW_MAX_LEN)
            g_shellCB.shellBufOffset = 0;

        PRT_SemPost(sem_uart_rx);
        flag = UART_REG_READ((unsigned long)(reg_base + 0x18));
    }
    return;
}

这个函数用于处理UART接收中断,将接收到的数据存储到缓冲区中,并通知相关任务数据已经到达。

flagresult 是用于存储从 UART 读取的数据。

reg_base 是 UART 的基地址,在初始化时设置。

flag 读取 UART 的接收状态寄存器。reg_base + 0x18 通常对应 UART 的接收状态寄存器(FR)。

(flag & (1 << 4)) == 0 检查 UART 的接收 FIFO 是否为空。如果第 4 位是 0,表示 FIFO 中有数据。

result 读取 UART 的数据寄存器(DR),通常位于 reg_base + 0x0 处。此寄存器包含接收到的字符。

g_shellCB.shellBuf 是接收数据的缓冲区。

g_shellCB.shellBufOffset 是当前缓冲区的位置,指向下一个可以存放数据的位置。

g_shellCB.shellBufOffset++ 增加缓冲区的偏移量。

如果缓冲区到达 SHELL_SHOW_MAX_LEN(缓冲区的最大长度),则将偏移量重置为 0,实现循环缓冲区。

PRT_SemPost(sem_uart_rx)sem_uart_rx 信号量发送信号,通知相关任务有新数据到达。相关任务通常会在这个信号量上等待数据。

在 src/bsp/prt_exc.c 中OsHwiHandleActive() 链接中断和处理函数OsUartRxHandle():

extern void OsTickDispatcher(void);
extern void OsUartRxHandle(void);
OS_SEC_ALW_INLINE INLINE void OsHwiHandleActive(U32 irqNum)
{
    switch(irqNum){
        case 30:
            OsTickDispatcher();
            // PRT_Printf(".");
            break;
        case 33:
            OsUartRxHandle();
        default:
            break;
    }
}

irqNum 为 30 时,调用 OsTickDispatcher 处理时钟中断。

irqNum 为 33 时,调用 OsUartRxHandle 处理 UART 接收中断。

在 src/kernel/task/prt_task.c 中加入函数:

extern U32 PRT_Printf(const char *format, ...);
OS_SEC_TEXT void OsDisplayTasksInfo(void)
{
    struct TagTskCb *taskCb = NULL;
    U32 cnt = 0;

    PRT_Printf("\nPID\t\tPriority\tStack Size\n");
    // 遍历g_runQueue队列,查找优先级最高的任务
    LIST_FOR_EACH(taskCb, &g_runQueue, struct TagTskCb, pendList) {
        cnt++;
        PRT_Printf("%d\t\t%d\t\t%d\n", taskCb->taskPid, taskCb->priority, taskCb->stackSize);
    }
    PRT_Printf("Total %d tasks", cnt);

}

OsDisplayTasksInfo 函数用于显示系统中所有正在运行的任务信息,包括任务 PID、优先级和栈大小。它遍历任务运行队列 g_runQueue,输出每个任务的相关信息并统计任务总数。

在 src/kernel/tick/prt_tick.c 中加入函数:

extern U32 PRT_Printf(const char *format, ...);
OS_SEC_TEXT void OsDisplayCurTick(void)
{
    PRT_Printf("\nCurrent Tick: %d", PRT_TickGetCount());
}

显示系统当前的滴答计数。

shell 处理

新建 src/shell/shmsg.c 文件:

#include "prt_typedef.h"
#include "prt_shell.h"
#include "os_attr_armv8_external.h"
#include "prt_task.h"
#include "prt_sem.h"

extern SemHandle sem_uart_rx;
extern U32 PRT_Printf(const char *format, ...);
extern void OsDisplayTasksInfo(void);
extern void OsDisplayCurTick(void);


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;
            }

        }
    }
}

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 函数实现了一个简单的命令行接口,通过UART接收用户输入的命令并执行相应的操作。这个函数目前能够处理两个命令:toptick

在主循环中,函数首先打印提示符 miniEuler #,然后初始化命令缓冲区 cmd 和索引 idx,准备接收用户输入。

内部循环中,函数调用 PRT_SemPend 等待 sem_uart_rx 信号量,表示有新的数据到达。

shellCB 缓冲区读取字符,并存储到命令缓冲区 cmd 中。同时,回显用户输入的字符。

当检测到回车键('\r')时,表示用户输入结束。函数检查命令缓冲区中的内容,并根据命令执行相应的操作。

  1. top 命令
    • 功能:显示当前系统中所有任务的信息。
    • 实现:当用户输入 top 并按下回车键时,函数调用 OsDisplayTasksInfo 函数来显示任务信息。
  2. tick 命令
    • 功能:显示当前系统时钟的滴答数。
    • 实现:当用户输入 tick 并按下回车键时,函数调用 OsDisplayCurTick 函数来显示当前的系统时钟滴答数。

作业

实现一条有用的 shell 指令。

首先在main函数中引用各种外部函数,启动Shell程序

extern U32 PRT_Printf(const char *format, ...);
extern void PRT_UartInit(void);
extern U32 OsActivate(void);
extern U32 OsTskInit(void);
extern U32 OsSemInit(void);
extern U32 OsHwiInit(void);
extern void ShellTask(uintptr_t param1, uintptr_t param2, uintptr_t param3, uintptr_t param4);
extern U32 ShellTaskInit(ShellCB *shellCB);
extern void CoreTimerInit(void);
extern ShellCB g_shellCB;
static SemHandle sem_sync;
static SemHandle sem_uart_rx;
.
.
.
OsHwiInit();
OsTskInit();
OsSemInit();
CoreTimerInit();
PRT_UartInit();
.
.
.
// 初始化 Shell 任务
ret = ShellTaskInit(&g_shellCB);
if (ret != OS_OK) {
     PRT_Printf("failed to initialize shell task\n");
     return ret;

实现结果如下:

image-20241030201749745

这里实现了三条额外的指令:

第一条是clear指令,用于清零Tick值,使Tick从0开始计数:

else if(cmd[0]=='c' && cmd[1]=='l' && cmd[2]=='e' && cmd[3]=='a' && cmd[4]=='r')
{
      OsClearTick();
}

OS_SEC_TEXT void OsClearTick(void)
{
    g_uniTicks = 0;
    PRT_Printf("Tick count cleared.\n");
    PRT_Printf("Current Tick: 0");
}

image-20241030201839500

第二条是Shell程序中常见的help指令,可以打印出所有的指令以及指令用途:

else if(cmd[0]=='h' && cmd[1]=='e' && cmd[2]=='l' && cmd[3]=='p'){
                 
		  	PRT_Printf("\ntop - Display tasks information");
			PRT_Printf("\ntick - Display current tick count");
			PRT_Printf("\nclear - Clear the tick");
			PRT_Printf("\nhelp - Display help message");
			PRT_Printf("\nquit - Exit the process");
			}

image-20241030201910583

第三条是quit,退出指令:

else if(cmd[0]=='q' && cmd[1]=='u' && cmd[2]=='i' && cmd[3]=='t')
{
		PRT_Printf("\nThank you for using it! Looking forward to next use!")	;
		flag=0;
}

image-20241030201947417

最后的main程序为:

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;
    int flag=1;
    while (flag) {
        PRT_Printf("\ngongming's miniEuler # ");
        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();
                }
                else if(cmd[0]=='c' && cmd[1]=='l' && cmd[2]=='e' && cmd[3]=='a' && cmd[4]=='r'){
                   OsClearTick();
                   }
                else if(cmd[0]=='h' && cmd[1]=='e' && cmd[2]=='l' && cmd[3]=='p'){
                 
		  	PRT_Printf("\ntop - Display tasks information");
			PRT_Printf("\ntick - Display current tick count");
			PRT_Printf("\nclear - Clear the tick");
			PRT_Printf("\nhelp - Display help message");
			PRT_Printf("\nquit - Exit the process");
			}
		else if(cmd[0]=='q' && cmd[1]=='u' && cmd[2]=='i' && cmd[3]=='t')
		{
		 	PRT_Printf("\nThank you for using it! Looking forward to next use!")	;
		  	flag=0;
		 }
	    
                break;
            }
    }
    }
}

标签:shellCB,UART,void,cmd,PRT,HNU,2022,Printf,lab9
From: https://blog.csdn.net/2301_76658831/article/details/143439407

相关文章

  • 20222407 2024-2025-1 《网络与系统攻防技术》实验四实验报告
    (一)实践目标恶意代码文件类型标识、脱壳与字符串提取对提供的rada恶意代码样本,进行文件类型识别,脱壳与字符串提取,以获得rada恶意代码的编写作者,具体操作如下:o使用文件格式和类型识别工具,给出rada恶意代码样本的文件格式、运行平台和加壳工具;o使用超级巡警脱壳机等脱壳软件,对rad......
  • Windows Server2022服务器部署RuoYi若依前后端分离
    部署准备虚拟机WindowsServer2022若依前后端分离v3.8.8打包好jdk1.8redis5mysql8.4iis服务路由插件重写插件1.安装jdk1.8https://www.azul.com/downloads/#downloads-table-zulu略2.安装启动redis5https://github.com/tporadowski/redis/releases下载安装默认配置......
  • 20222416 2024-2025-1 《网络与系统攻防技术》实验四实验报告
    1.实验内容1.1本周学习内容恶意代码是使计算机按照攻击者的意图运行以达到恶意目的的指令集合。类型有计算机病毒,蠕虫,恶意移动代码,后门,特洛伊木马,僵尸程序,Rootkit(内核套件),融合型恶意代码等。分析恶意代码的方式通常有系统监控、静态分析和动态分析等方法。这里展示......
  • 20222423 2024-2025-1 《网络与系统攻防技术》实验四实验报告
    1.实验内容一、恶意代码文件类型标识、脱壳与字符串提取对提供的rada恶意代码样本,进行文件类型识别,脱壳与字符串提取,以获得rada恶意代码的编写作者,具体操作如下:(1)使用文件格式和类型识别工具,给出rada恶意代码样本的文件格式、运行平台和加壳工具;(2)使用超级巡警脱壳机等脱壳软件......
  • 20222427 2024-2025-1 《网络与系统攻防技术》实验四实验报告
    1.实验内容1.1本周学习内容学会了有关恶意代码分析的技术的基础知识,包括静态分析与动态分析。尝试运用静态分析方法去实现一些目的行为。学习了有关于信息收集的有关知识,包括相应的定义、收集方式以及实现收集的技术。1.2实验内容恶意代码文件类型标识、脱壳与......
  • 20222424 2024-2025-1 《网络与系统攻防技术》实验四实验报告
    202224242024-2025-1《网络与系统攻防技术》实验四实验报告1.实验内容恶意代码文件类型标识、脱壳与字符串提取。使用IDAPro静态或动态分析,寻找特定输入,使其能够输出成功信息。分析恶意代码样本rada,并撰写报告。取证分析实践——Windows2000系统被攻破并加入僵尸网络2......
  • Visual Studio 2022安装水晶报表(Crystal Reports)
    这些天,为程序呈现报表。当想添加CrystalReport时,发现VisualStudio2022无法添加报表。只有重新下载啦。去这里https://www.tektutorialshub.com/crystal-reports/download-crystal-reports-for-visual-studio-2022/下载 上面图片,标志1,即是安装至vs2022。而标志2,是运行环境所......
  • # 20222419 2024-2025-1 《网络与系统攻防技术》实验四实验报告
    1.实验内容一、恶意代码文件类型标识、脱壳与字符串提取对提供的rada恶意代码样本,进行文件类型识别,脱壳与字符串提取,以获得rada恶意代码的编写作者,具体操作如下:(1)使用文件格式和类型识别工具,给出rada恶意代码样本的文件格式、运行平台和加壳工具;(2)使用超级巡警脱壳机等脱壳软件,......
  • 20222325 2024-2025-1 《网络与系统攻防技术》实验四实验报告
    1.实验内容一、恶意代码文件类型标识、脱壳与字符串提取对提供的rada恶意代码样本,进行文件类型识别,脱壳与字符串提取,以获得rada恶意代码的编写作者,具体操作如下:(1)使用文件格式和类型识别工具,给出rada恶意代码样本的文件格式、运行平台和加壳工具;(2)使用超级巡警脱壳机等脱壳软件,......