从0开始的操作系统手搓教程43——实现一个简单的shell

news2025/3/12 23:17:26

目录

添加 read 系统调用,获取键盘输入 :sys_read

putchar和clear

上班:实现一个简单的shell

测试上电


我们下面来实现一个简单的shell

添加 read 系统调用,获取键盘输入 :sys_read

/* Read count bytes from the file pointed to by file descriptor fd into buf,
 * return the number of bytes read on success, return -1 if end of file is
 * reached
 */
int32_t sys_read(int32_t fd, void *buf, uint32_t count)
{
    KERNEL_ASSERT(buf);
    int32_t ret = -1;
    uint32_t global_fd = 0;
    if (fd < 0 || fd == stdout_no || fd == stderr_no)
    {
        ccos_printk("sys_read: fd error\n");
    }
    else if (fd == stdin_no)
    {
        char *buffer = buf;
        uint32_t bytes_read = 0;
        while (bytes_read < count)
        {
            *buffer = ioq_getchar(&keyboard_ringbuffer);
            bytes_read++;
            buffer++;
        }
        ret = (bytes_read == 0 ? -1 : (int32_t)bytes_read);
    }
    else
    {
        global_fd = fd_local2global(fd);
        ret = file_read(&file_table[global_fd], buf, count);
    }
    return ret;
}

我们终于跟老朋友见面了。我们修订一下:sys_read,现在实现了读取文件的功能。它首先检查传入的 buf 指针是否有效。如果文件描述符 fd 无效(例如小于0或者是标准输出、标准错误),则会打印错误信息。如果文件描述符是标准输入(stdin_no),函数会从键盘输入的缓冲区 keyboard_ringbuffer 中读取字符,将其存入 buf 中,直到读取指定的字节数或达到文件末尾。函数会返回成功读取的字节数,如果没有读取到任何数据,返回 -1。如果文件描述符不是标准输入,函数会根据 fd 获取对应的文件,并调用 file_read 函数来执行实际的读取操作,并返回成功读取的字节数。如果文件读取失败,返回 -1

int32_t read(int32_t fd, void *buf, uint32_t count)
{
    return _syscall3(SYS_READ, fd, buf, count);
}

putchar和clear

这个把我们之前做的工作编程系统调用即可

/* Outputs a character */
void putchar(char char_asci) {
    _syscall1(SYS_PUTCHAR, char_asci);
}
/* Clears the screen */
void clear(void) {
    _syscall0(SYS_CLEAR);
}
​
void sys_putchar(char char_asci)
{
    console__ccos_putchar(char_asci);
}
​
​
/* Initialize the system call table */
void syscall_init(void) {
    verbose_ccputs(
        "syscall_init start\n"); // Logging the start of syscall initialization
    /* Set the system call table entries for various system call numbers */
    syscall_table[SYS_GETPID] = sys_getpid; // Get process ID
    syscall_table[SYS_WRITE] = sys_write;   // Write to console
    syscall_table[SYS_MALLOC] = sys_malloc; // Memory allocation
    syscall_table[SYS_FREE] = sys_free;     // Free allocated memory
    syscall_table[SYS_FORK] = sys_fork;     // Fork a new process
    syscall_table[SYS_READ] = sys_read;
    syscall_table[SYS_PUTCHAR] = sys_putchar;
    syscall_table[SYS_CLEAR] = clean_screen;
    verbose_ccputs("syscall_init done\n"); // Logging the completion of syscall
                                          // initialization
}

上班:实现一个简单的shell

#include "include/user/ccshell/ccshell.h"
#include "include/defines.h"
#include "include/filesystem/file.h"
#include "include/library/string.h"
#include "include/library/types.h"
#include "include/syscall/syscall.h"
#include "include/user/stdio/stdio.h"
#include "include/filesystem/filesystem_settings.h"
#include "include/user/library/user_assertion.h"
#define CMD_LEN MAX_PATH_LEN
#define MAX_ARG_NR (16)
​
/* Stores the input command */
static char cmd_line[CMD_LEN] = {0};
​
/* Used to record the current directory; it is updated every time the cd command
 * is executed */
char cwd_cache[MAX_PATH_LEN] = {0};
​
/* Outputs the shell prompt */
void print_prompt(void)
{
    printf("[" HOST_NAME "@localhost %s]$ ", cwd_cache);
}
​
/* Reads up to 'count' bytes from the keyboard buffer into 'buf' */
static void readline(char *buf, int32_t count)
{
    user_assert(buf && count > 0);
    char *pos = buf;
​
    while (read(stdin_no, pos, 1) != -1 &&
           (pos - buf) < count)
    { // Read until enter key is found
        switch (*pos)
        {
            /* If enter or newline is found, treat it as the end of the command
             */
        case '\n':
        case '\r':
            *pos = 0; // Add null terminator to cmd_line
            putchar('\n');
            return;
​
        case '\b':
            if (cmd_line[0] != '\b')
            {          // Prevent deleting non-inputted data
                --pos; // Move back to the previous character in the buffer
                putchar('\b');
            }
            break;
​
        /* For other characters, output normally */
        default:
            putchar(*pos);
            pos++;
        }
    }
    printf("readline: can't find enter_key in the cmd_line, max num of char is "
           "128\n");
}

先说说readline:这段代码实现了一个从键盘缓冲区读取用户输入的功能。函数readline的作用是从输入流中读取字符,直到遇到回车键(\n\r)为止,或者读取达到指定的字节数限制。读取的内容会存储到buf中。具体步骤如下:

  1. 函数首先检查传入的缓冲区指针buf是否有效,并确认count大于0。

  2. while循环会持续读取一个字符(通过read(stdin_no, pos, 1)),直到读取到回车键或达到指定的字符数量(count)。读取的字符会存储在buf中。

  3. 当遇到回车符(\n\r)时,表示用户输入完成,此时将当前位置字符设置为'\0',并输出换行符,表示命令输入结束。

  4. 如果遇到退格符(\b),程序会检查输入内容是否为空。如果不为空,指针会退回一个字符,删除命令行中的最后一个字符,并输出退格符。此功能允许用户删除输入的字符。

  5. 其他字符直接输出并追加到pos指向的位置,继续读取下一个字符。

  6. 如果没有在指定字符数内读取到回车键,则会输出错误提示,表明命令行输入超出了最大字符限制。

这个函数的设计使得用户可以在命令行中输入命令,且支持基本的退格功能,直到按下回车键为止。

ccshell是我们的核心:

void ccshell(void)
{
    cwd_cache[0] = '/';
    while (1)
    {
        print_prompt();
        k_memset(cmd_line, 0, CMD_LEN);
        readline(cmd_line, CMD_LEN);
        if (cmd_line[0] == 0)
        {
            continue;
        }
    }
    user_panic("Man!: you should not be here!!!");
}

这段代码实现了一个简单的命令行shell功能。函数ccshell定义了一个无限循环,其中每次循环都会显示提示符,等待用户输入命令。命令输入通过readline函数完成,读取用户输入并存储到cmd_line中。每次读取命令之前,cmd_line会被清空,确保新命令不会受到旧命令的影响。如果用户没有输入命令,程序会跳过本次循环。当用户输入命令时,如果命令不为空,程序会继续执行。虽然代码没有明确的退出机制,但如果程序异常执行到user_panic("Man!: you should not be here!!!"),说明发生了错误。

测试上电

#include "include/device/console_tty.h"
#include "include/kernel/init.h"
#include "include/library/kernel_assert.h"
#include "include/thread/thread.h"
#include "include/user/stdio/stdio.h"
#include "include/memory/memory.h"
#include "include/library/ccos_print.h"
#include "include/filesystem/filesystem.h"
#include "include/library/string.h"
#include "include/filesystem/dir.h"
#include "include/syscall/syscall.h"
#include "include/user/ccshell/ccshell.h"
void init(void);
​
int main(void)
{
    init_all();
    while(1);
}
​
// init process here
void init(void)
{
    uint32_t ret_pid = fork();
    if (ret_pid)
    {
        while(1);
    }
    else
    {
        ccshell();
    }
    while (1)
        ;
}
​

注意多敲几下键盘,因为输出非常多。

下一篇

从0开始的操作系统手搓教程44——实现更好的shell-CSDN博客文章浏览阅读766次,点赞14次,收藏9次。在 Linux 系统中,快捷键如“Ctrl+u”和“Ctrl+l”是由操作系统提供的标准功能,但它们的实现方式并不是直接由键盘驱动程序(keyboard.c)来处理的。如果在键盘驱动程序中加入过多的逻辑处理,会导致系统的中断处理变得非常复杂,效率降低,进而影响整个系统的响应速度。的数据类型(从16位改为32位),因为一旦改动数据类型,后续代码可能会受到影响,造成所谓的“雪崩效应”,即需要改动很多地方的代码和解释。时,输入缓冲区中的字符会被清除,屏幕会被清空,然后打印命令提示符,再显示用户已输入的命令。 https://blog.csdn.net/charlie114514191/article/details/146144528

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2313983.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Spring】基础/体系结构/核心模块

概述&#xff1a; Spring 是另一个主流的 Java Web 开发框架&#xff0c;该框架是一个轻量级的应用框架。 Spring 是分层的 Java SE/EE full-stack 轻量级开源框架&#xff0c;以 IoC&#xff08;Inverse of Control&#xff0c;控制反转&#xff09;和 AOP&#xff08;Aspect…

01 音视频知识学习(视频)

图像基础概念 ◼像素&#xff1a;像素是一个图片的基本单位&#xff0c;pix是英语单词picture的简写&#xff0c;加上英 语单词“元素element”&#xff0c;就得到了“pixel”&#xff0c;简称px&#xff0c;所以“像素”有“图像元素” 之意。 ◼ 分辨率&#xff1a;是指图像…

vue3自定义hooks遇到的问题

问题 写了一个输入查询参数和url返回加载中状态、请求方法、接口返回列表的hooks&#xff0c;出现的结果是只有请求方法有效&#xff0c;加载状态无效&#xff0c;接口返回了数据&#xff0c;页面却不显示数据。 代码如下 只展示部分关键代码 import { ref, toRefs, toRef, o…

liunx磁盘挂载和jar启动命令

一、磁盘挂载 查看历史磁盘挂载命令&#xff1a;history | grep mount 查看所有挂载硬盘命令&#xff1a;mount 磁盘挂载命令&#xff1a;mount -t cifs -o usernamesh**,passwordP!ss**** //192.168.1.2/attachmentfilesShare2.2/pdfCert /home/nybzg/cnfai1/pdfCert 二、j…

gbase8s rss集群通信流程

什么是rss RSS是一种将数据从主服务器复制到备服务器的方法 实例级别的复制 (所有启用日志记录功能的数据库) 基于逻辑日志的复制技术&#xff0c;需要传输大量的逻辑日志,数据库需启用日志模式 通过网络持续将数据复制到备节点 如果主服务器发生故障&#xff0c;那么备用服务…

如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统

我在业余时间开发了一款自己的独立产品&#xff1a;升讯威在线客服与营销系统。陆陆续续开发了几年&#xff0c;从一开始的偶有用户尝试&#xff0c;到如今线上环境和私有化部署均有了越来越多的稳定用户。 随时近来 AI 大模型的火热&#xff0c;越来越多的客户&#xff0c;问…

【AI智能体报告】开源AI助手的革命:OpenManus深度使用报告

一、引言&#xff1a;当开源智能体走进生活 2025年3月&#xff0c;MetaGPT团队用一场"开源闪电战"改写了AI Agent的竞争格局。面对商业产品Manus高达10万元的邀请码炒作&#xff0c;他们仅用3小时便推出开源替代品OpenManus&#xff0c;首日即登顶GitHub趋势榜。 …

DeepSeek+Maxkb+Ollama+Docker搭建一个AI问答系统

DeepSeekMaxkbOllamaDocker搭建一个AI问答系统 文章目录 DeepSeekMaxkbOllamaDocker搭建一个AI问答系统前言一、创建同一内网的网络二、拉取两个镜像三、启动Ollama以及调试Maxkb4.Maxkb创建一个应用并建立知识库5、应用效果总结 前言 我觉得只要是使用Docker技术&#xff0c;…

江科大51单片机笔记【12】DS18B20温度传感器(上)

写在前言 此为博主自学江科大51单片机&#xff08;B站&#xff09;的笔记&#xff0c;方便后续重温知识 在后面的章节中&#xff0c;为了防止篇幅过长和易于查找&#xff0c;我把一个小节分成两部分来发&#xff0c;上章节主要是关于本节课的硬件介绍、电路图、原理图等理论…

P8662 [蓝桥杯 2018 省 AB] 全球变暖--DFS

P8662 [蓝桥杯 2018 省 AB] 全球变暖--dfs 题目 解析讲下DFS代码 题目 解析 这道题的思路就是遍历所有岛屿&#xff0c;判断每一块陆地是否会沉没。对于这种图的遍历&#xff0c;我们首先应该想到DFS。 代码的注意思想就是&#xff0c;在主函数中遍历找出所有岛屿&#xff0c…

Vue 侧边栏导航栏 el-menu单个item和多个item

在固钉的下面去写菜单导航栏。 <el-menu class"aside-menu" router :default-active"$route.path" :collapse"isCollapse" background-color"#131b27" text-color"#bfcbd9" active-text-color"#20a0ff" :defau…

Unity Dots从入门到精通之 Prefab引用 转 实体引用

文章目录 前言安装 DOTS 包实体引用Authoring 前言 DOTS&#xff08;面向数据的技术堆栈&#xff09;是一套由 Unity 提供支持的技术&#xff0c;用于提供高性能游戏开发解决方案&#xff0c;特别适合需要处理大量数据的游戏&#xff0c;例如大型开放世界游戏。 本文讲解我在…

无人机避障——XTDrone中运行VINS-Fusion+Ego-planner进行路径规划

本文聚焦于无人机避障技术领域的经典方案&#xff0c;重点探讨视觉双目VINS-Fusion建图与Ego-planner路径规划的组合应用。通过视觉双目VINS-Fusion实现精准的环境建图与自身定位&#xff0c;结合Ego-planner的高效路径规划能力&#xff0c;使无人机在复杂环境中实现自主避障飞…

【沐渥科技】氮气柜日常如何维护?

氮气柜的维护是确保其长期稳定运行、延长使用寿命和保持环境控制精度的关键。以下是沐渥氮气柜的日常维护和定期保养指南&#xff1a; 一、日常维护 柜体清洁 定期用软布擦拭柜体表面和内部&#xff0c;避免灰尘堆积。避免使用腐蚀性清洁剂&#xff0c;防止损伤密封条或传感器。…

MATLAB 控制系统设计与仿真 - 24

PID 控制器分析- 控制器的形式 连续控制器的结构&#xff1a; 为滤波时间常数&#xff0c;这类PID控制器在MATLAB系统控制工具箱称为并联PID控制器&#xff0c;可由MATLAB提供的pid函数直接输入&#xff0c;格式为&#xff1a; 其他类型的控制器也可以由该函数直接输入&#x…

linux(权限)

sudo 主要用来短暂的提权 权限 就是 >角色目标属性 这里面的角色就是---拥有者----所属组----other 所属组的目的&#xff1f; 更细化的管理 chmod 就是修改权限制 我们要是想要切换到体育的账号&#xff0c;我们可以去看一下有几个账号,我…

《OpenCV》—— dlib(换脸操作)

文章目录 dlib换脸介绍仿射变换在 dlib 换脸中的应用 换脸操作 dlib换脸介绍 dlib 换脸是基于 dlib 库实现的一种人脸替换技术&#xff0c;以下是关于它的详细介绍&#xff1a; 原理 人脸检测&#xff1a;dlib 库中包含先进的人脸检测器&#xff0c;如基于 HOG&#xff08;方向…

修改Flutter项目使用的JAVA版本

使用Android studio开发Flutter过程中&#xff0c;会默认使用Android studio自带的JDK。因为新版Android studio中的JDK版本过高&#xff0c;导致项目编译时总是无法完成&#xff0c;报【 unsupported class file major version 65】错误&#xff0c;如下&#xff1a; 解决这个…

虚拟dom的diff中的双端比较算法

‌双端比较算法是Vue中用于高效比较新旧VNode子节点的一种策略‌。该算法的核心思想是&#xff0c;通过从新旧VNode子节点的两端开始比较&#xff0c;逐步向中间靠拢&#xff0c;以找到最小的差异并据此更新DOM。以下是双端比较算法的大致流程&#xff1a; ‌初始化指针‌&…

VMware安装Windows server 2016

1、新建虚拟机&#xff0c;选择自定义模式 2、选择兼容性 4、命名虚拟机 5、固件类型 EFI 虚拟磁盘类型&#xff0c;不同电脑推荐的类型不同&#xff0c;用默认的就行 删除声卡和打印机 检查网络配置 选择本地的Windows server 2016的系统镜像&#xff0c;系统镜像可以去Window…