鸿蒙内核源码分析(Shell编辑篇) | 两个任务,三个阶段

系列篇从内核视角用一句话概括shell的底层实现为:两个任务,三个阶段。其本质是独立进程,因而划到进程管理模块。每次创建shell进程都会再创建两个任务。

  • 客户端任务(ShellEntry): 负责接受来自终端(控制台)敲入的一个个字符,字符按VT规范组装成一句句的命令。
  • 服务端任务(ShellTask): 对命令进行解析并执行,将结果输出到控制台。

而按命令生命周期可分三个阶段.

  • 编辑: 鸿蒙在这个部分实现了一个简单的编辑器功能,处理控制台输入的每个字符,主要包括了对控制字符 例如 <ESC>,\t,\b,\n,\r,四个方向键0x41 ~ 0x44 的处理。
  • 解析: 对编辑后的字符串进行解析,解析出命令项和参数项,找到对应的命令项执行函数。
  • 执行: 命令可通过静态和动态两种方式注册到内核,解析出具体命令后在注册表中找到对应函数回调。将结果输出到控制台。

编辑部分由客户端任务完成,后两个部分由服务端任务完成,命令全局注册由内核完成。

  • 本篇主要说 客户端任务 和 编辑过程
  • 服务端任务 和 解析/执行过程 已在 (Shell解析篇) 中说明,请自行翻看.

什么是 Shell

从用户视角看,shell是用户窥视和操作内核的一个窗口,内核并非铁板一块,对应用层开了两个窗口,一个是系统调用,一个就是shell,由内核提供实现函数,由用户提供参数执行。区别是 shell是由独立的任务去完成,可通过将shell命令序列化编写成独立的,简单的shell程序,所以shell也是一门脚本语言,系统调用是依附于应用程序的任务去完成,能做的有限。通过shell窗口能看到 cpu的运行情况,内存的消耗情况,网络的链接状态等等。

鸿蒙 Shell 代码在哪

shell对应的概念是kernel,在鸿蒙内核,这两部分代码是分开放的,shell代码在 查看 shell 代码 ,目录结构如下.

├─include
│      dmesg.h
│      dmesg_pri.h
│      shcmd.h
│      shcmdparse.h
│      shell.h
│      shell_lk.h
│      shell_pri.h
│      shmsg.h
│      show.h
│
└─src├─base│      shcmd.c│      shcmdparse.c│      shell_lk.c│      shmsg.c│      show.c│└─cmdsdate_shellcmd.cdmesg.chwi_shellcmd.cshell_shellcmd.cwatch_shellcmd.c

Shell 控制块

跟进程,任务一样,每个概念的背后需要一个主结构体来的支撑,shell的主结构体就是ShellCB,掌握它就可以将shell拿捏的死死的,搞不懂这个结构体就读不懂shell的内核实现.所以在上面花再多功夫也不为过.

typedef struct {UINT32   consoleID;	//控制台IDUINT32   shellTaskHandle;	//shell服务端任务IDUINT32   shellEntryHandle;	//shell客户端任务IDVOID     *cmdKeyLink;		//待处理的shell命令链表VOID     *cmdHistoryKeyLink;//已处理的历史记录链表,去重,10个VOID     *cmdMaskKeyLink;	//主要用于方向键上下遍历历史命令UINT32   shellBufOffset;	//buf偏移量UINT32   shellKeyType;	//按键类型EVENT_CB_S shellEvent;	//事件类型触发pthread_mutex_t keyMutex;	//按键互斥量pthread_mutex_t historyMutex;	//历史记录互斥量CHAR     shellBuf[SHOW_MAX_LEN];	//shell命令buf,接受键盘的输入,需要对输入字符解析.CHAR     shellWorkingDirectory[PATH_MAX];//shell的工作目录
} ShellCB;
//一个shell命令的结构体,命令有长有短,鸿蒙采用了可变数组的方式实现
typedef struct {UINT32 count;	//字符数量LOS_DL_LIST list;	//双向链表CHAR cmdString[0];	//字符串,可变数组的一种实现方式.
} CmdKeyLink;enum {STAT_NOMAL_KEY,	//普通的按键STAT_ESC_KEY,	//<ESC>键在VT控制规范中时控制的起始键STAT_MULTI_KEY	//组合键
};

解读

  • 鸿蒙支持两种方式在控制台输入Shell命令,关于控制台请自行翻看控制台篇.
    • 在串口工具中直接输入Shell命令 CONSOLE_SERIAL
    • telnet工具中输入Shell命令 CONSOLE_TELNET
  • shellTaskHandleshellEntryHandle编辑/处理shell命令的两个任务ID,本篇重点说后一个.
  • cmdKeyLink,cmdHistoryKeyLink,cmdMaskKeyLink是三个类型为CmdKeyLink的结构体,本质是双向链表,对应编辑shell命令过程中的三个功能.
    • cmdKeyLink 待执行的命令链表
    • cmdHistoryKeyLink 存储命令历史记录的,即: history命令显示的内容
    • cmdMaskKeyLink 记录按上下方向键输出的内容,这个有点难理解,自行在shell中按上下方向键自行体验
  • shellBufOffsetshellBuf是成对出现的,其中存放的就是用户敲入处理后的字符.
  • keyMutexhistoryMutex为操作链表所需的互斥锁,内核用的最多的就是这类锁.
  • shellEvent用于任务之间的通讯,比如.
    • SHELL_CMD_PARSE_EVENT:编辑完成了通知解析任务开始执行
    • CONSOLE_SHELL_KEY_EVENT:收到来自控制台的CTRL + C信号产生的事件.
  • shellKeyType 按键的类型,分三种 普通,键,组合键
  • shellWorkingDirectory 工作区就不用说了,从哪个目录进入shell

创建 Shell

//shell进程的入口函数
int main(int argc, char **argv)
{//...g_shellCB = shellCB;//全局变量,说明鸿蒙同时只支持一个shell进程return OsShellCreateTask(shellCB);//初始化两个任务
}
//创建shell任务
STATIC UINT32 OsShellCreateTask(ShellCB *shellCB)
{UINT32 ret = ShellTaskInit(shellCB);//执行shell命令的任务初始化if (ret != LOS_OK) {return ret;}return ShellEntryInit(shellCB);//通过控制台接收shell命令的任务初始化
}
//进入shell客户端任务初始化,这个任务负责编辑命令,处理命令产生的过程,例如如何处理方向键,退格键,回车键等
LITE_OS_SEC_TEXT_MINOR UINT32 ShellEntryInit(ShellCB *shellCB)
{UINT32 ret;CHAR *name = NULL;TSK_INIT_PARAM_S initParam = {0};if (shellCB->consoleID == CONSOLE_SERIAL) {name = SERIAL_ENTRY_TASK_NAME;} else if (shellCB->consoleID == CONSOLE_TELNET) {name = TELNET_ENTRY_TASK_NAME;} else {return LOS_NOK;}initParam.pfnTaskEntry = (TSK_ENTRY_FUNC)ShellEntry;//任务入口函数initParam.usTaskPrio   = 9; /* 9:shell task priority */initParam.auwArgs[0]   = (UINTPTR)shellCB;initParam.uwStackSize  = 0x1000;initParam.pcName       = name;initParam.uwResved     = LOS_TASK_STATUS_DETACHED;ret = LOS_TaskCreate(&shellCB->shellEntryHandle, &initParam);//创建任务
#ifdef LOSCFG_PLATFORM_CONSOLE(VOID)ConsoleTaskReg((INT32)shellCB->consoleID, shellCB->shellEntryHandle);//将任务注册到控制台
#endifreturn ret;
}

解读

  • mainshell进程的主任务,每个进程都会创建一个默认的线程(任务),这个任务的入口函数就是大家熟知的main函数,不清楚的自行翻看任务管理各篇有详细的说明.
  • main任务再创建两个任务,即本篇开头说的两个任务,本篇重点说其中的一个 ShellEntry,任务优先级为9,算是较高优先级.
  • 指定内核栈大小为0x1000 = 4K ,因任务只负责编辑处理控制台输入的字符,命令的执行在其他任务,所以4K的内核空间足够使用.
  • ShellEntry为入口函数,这个函数的实现为本篇的重点

ShellEntry | 编辑过程

LITE_OS_SEC_TEXT_MINOR UINT32 ShellEntry(UINTPTR param)
{CHAR ch;INT32 n = 0;ShellCB *shellCB = (ShellCB *)param;CONSOLE_CB *consoleCB = OsGetConsoleByID((INT32)shellCB->consoleID);//获取控制台if (consoleCB == NULL) {PRINT_ERR("Shell task init error!\n");return 1;}(VOID)memset_s(shellCB->shellBuf, SHOW_MAX_LEN, 0, SHOW_MAX_LEN);//重置shell命令bufwhile (1) {
#ifdef LOSCFG_PLATFORM_CONSOLEif (!IsConsoleOccupied(consoleCB)) {//控制台是否被占用
#endif/* is console ready for shell ? */n = read(consoleCB->fd, &ch, 1);//从控制台读取一个字符内容,字符一个个处理if (n == 1) {//如果能读到一个字符ShellCmdLineParse(ch, (pf_OUTPUT)dprintf, shellCB);}if (is_nonblock(consoleCB)) {//在非阻塞模式下暂停 50msLOS_Msleep(50); /* 50: 50MS for sleep */}
#ifdef LOSCFG_PLATFORM_CONSOLE}
#endif}
}
//对命令行内容解析
LITE_OS_SEC_TEXT_MINOR VOID ShellCmdLineParse(CHAR c, pf_OUTPUT outputFunc, ShellCB *shellCB)
{const CHAR ch = c;INT32 ret;//不是回车键和字符串结束,且偏移量为0if ((shellCB->shellBufOffset == 0) && (ch != '\n') && (ch != '\0')) {(VOID)memset_s(shellCB->shellBuf, SHOW_MAX_LEN, 0, SHOW_MAX_LEN);//重置buf}//遇到回车或换行if ((ch == '\r') || (ch == '\n')) {if (shellCB->shellBufOffset < (SHOW_MAX_LEN - 1)) {shellCB->shellBuf[shellCB->shellBufOffset] = '\0';//字符串结束}shellCB->shellBufOffset = 0;(VOID)pthread_mutex_lock(&shellCB->keyMutex);OsShellCmdPush(shellCB->shellBuf, shellCB->cmdKeyLink);//解析回车或换行(VOID)pthread_mutex_unlock(&shellCB->keyMutex);ShellNotify(shellCB);//通知任务解析shell命令return;} else if ((ch == '\b') || (ch == 0x7F)) { /* backspace or delete(0x7F) */ //遇到删除键if ((shellCB->shellBufOffset > 0) && (shellCB->shellBufOffset < (SHOW_MAX_LEN - 1))) {shellCB->shellBuf[shellCB->shellBufOffset - 1] = '\0';//填充`\0`shellCB->shellBufOffset--;//buf减少outputFunc("\b \b");//回调入参函数}return;} else if (ch == 0x09) { /* 0x09: tab *///遇到tab键if ((shellCB->shellBufOffset > 0) && (shellCB->shellBufOffset < (SHOW_MAX_LEN - 1))) {ret = OsTabCompletion(shellCB->shellBuf, &shellCB->shellBufOffset);//解析tab键if (ret > 1) {outputFunc("OHOS # %s", shellCB->shellBuf);//回调入参函数}}return;}/* parse the up/down/right/left key */ret = ShellCmdLineCheckUDRL(ch, shellCB);//解析上下左右键if (ret == LOS_OK) {return;}if ((ch != '\n') && (ch != '\0')) {//普通的字符的处理if (shellCB->shellBufOffset < (SHOW_MAX_LEN - 1)) {//buf范围shellCB->shellBuf[shellCB->shellBufOffset] = ch;//直接加入} else {shellCB->shellBuf[SHOW_MAX_LEN - 1] = '\0';//加入字符串结束符}shellCB->shellBufOffset++;//偏移量增加outputFunc("%c", ch);//向终端输出字符}shellCB->shellKeyType = STAT_NOMAL_KEY;//普通字符
}

解读

  • ShellEntry内部是个死循环,不断的读取控制台输入的每个字符,注意是按字符处理.
  • 处理四个方向,换行回车,tab,backspace,delete,esc 等控制键,相当于重新认识了下Ascii表.可以把shell终端理解为一个简单的编辑器.
    • 按回车键 表示完成前面的输入,进入解析执行阶段.
    • 按方向键 要显示上/下一个命令的内容,一直按就一直显示上上/下下命令.
    • tab键 是要补齐命令的内容,目前鸿蒙支持如下命令:
          arp           cat           cd            chgrp         chmod         chown         cp            cpup          date          dhclient      dmesg         dns           format        free          help          hwi           ifconfig      ipdebug       kill          log           ls            lsfd          memcheck      mkdir         mount         netstat       oom           partinfo      partition     ping          ping6         pwd           reset         rm            rmdir         sem           statfs        su            swtmr         sync          systeminfo    task          telnet        test          tftp          touch         umount        uname         watch         writeproc  

例如:当在控制台按下 ch和 tab键后会输出以下三个

        chgrp         chmod         chown

内容,这些功能对使用者而已看似再平常不过,但都需要内核一一实现.

  • shellBuf存储编辑结果,当按下回车键时,将结果保存并交付给下一个阶段使用.

鸿蒙全栈开发全新学习指南

也为了积极培养鸿蒙生态人才,让大家都能学习到鸿蒙开发最新的技术,针对一些在职人员、0基础小白、应届生/计算机专业、鸿蒙爱好者等人群,整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线【包含了大厂APP实战项目开发】

本路线共分为四个阶段:

第一阶段:鸿蒙初中级开发必备技能

第二阶段:鸿蒙南北双向高工技能基础:gitee.com/MNxiaona/733GH

第三阶段:应用开发中高级就业技术

第四阶段:全网首发-工业级南向设备开发就业技术:gitee.com/MNxiaona/733GH

《鸿蒙 (Harmony OS)开发学习手册》(共计892页)

如何快速入门?

1.基本概念
2.构建第一个ArkTS应用
3.……

开发基础知识:gitee.com/MNxiaona/733GH

1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……

基于ArkTS 开发

1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……

鸿蒙开发面试真题(含参考答案):gitee.com/MNxiaona/733GH

鸿蒙入门教学视频:

美团APP实战开发教学:gitee.com/MNxiaona/733GH

写在最后

  • 如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
  • 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
  • 想要获取更多完整鸿蒙最新学习资源,请移步前往小编:gitee.com/MNxiaona/733GH

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

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

相关文章

【C/C++】设计模式——工厂模式:简单工厂、工厂方法、抽象工厂

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; &#x1f525;c系列专栏&#xff1a;C/C零基础到精通 &#x1f525; 给大…

品鉴中的个人风格:如何形成自己与众不同的红酒品鉴体验

品鉴云仓酒庄雷盛红酒不仅是一种感官体验&#xff0c;更是一种个人风格的展现。每个人都有自己与众不同的品味和偏好&#xff0c;通过品鉴红酒&#xff0c;我们可以形成自己与众不同的红酒品鉴体验。 要形成自己与众不同的红酒品鉴体验&#xff0c;首先需要勇于尝试不同类型的红…

天机学堂—项目总览和基建

总览 天机学堂是一个基于微服务架构的生产级在线教育项目。 项目亮点 技术架构 环境搭建 为了模拟真实开发环境&#xff0c;我们准备了一台虚拟机&#xff0c;在其中安装了各种各样的公共服务和组件。 Jenkins 是一个开源的持续集成&#xff08;Continuous Integration&…

计算机毕业设计Python地震预测系统 地震数据分析可视化 地震爬虫 大数据毕业设计 Flink Hadoop 深度学习 机器学习 人工智能 知识图谱

学生信息 姓名&#xff1a;  祁浩 题目&#xff1a; 基于Python的中国地震数据分析与可视化系统的设计与实现 学号&#xff1a; 2020135211 班级&#xff1a; 20大数据本科2班 指导教师&#xff1a; 刘思思 答辩过程 学生开题陈述 为了让学习者更好的了解了解地震…

SpringBoot 使用logback(多环境配置)

Logback是由log4j创始人设计的又一个开源日志组件。可用于项目日志功能。官网地址 第1步&#xff1a;添加坐标依赖 <!--logback--> <dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version…

使用Flask-Admin创建强大的后台管理系统

文章目录 安装Flask-Admin创建Flask应用添加Flask-Admin添加模型扩展延伸自定义视图权限管理文件上传 结语 在Web应用开发中&#xff0c;后台管理系统是至关重要的组成部分&#xff0c;它能够让管理员轻松管理应用的各种数据和配置。Flask-Admin是一个功能强大的Flask扩展&…

【DevOps】Linux 安全:iptables 组成、命令及应用场景详解

导读&#xff1a;全面掌握 iptables&#xff1a;从基础到实践 在 Linux 系统中&#xff0c;iptables 是一个非常强大的工具&#xff0c;它不仅是系统管理员用来构建和管理网络防火墙的首选工具&#xff0c;而且也是一个功能丰富的网络流量处理系统。无论是进行包过滤、监控网络…

ROS 2边学边练(46)-- 给URDF模型添加物理属性和碰撞属性

前言 实际物理世界中的物体需要遵守物理定律&#xff0c;机器人也不例外&#xff0c;在urdf中我们同样可以给机器人添加相关属性以模拟对应真实物理世界中的情况。 动动手 碰撞collision 碰撞属性指的是该物体的最小安全物理空间&#xff0c;站在机器人的角度&#xff0c;这个…

UE5C++ FString做为参数取值时报错error:C4840

问题描述 用来取FString类型的变量时报错&#xff1a; 问题解决 点击错误位置&#xff0c;跳转到代码&#xff1a; void AMyDelegateActor::TwoParamDelegateFunc(int32 param1, FString param2) {UE_LOG(LogTemp, Warning, TEXT("Two Param1:%d Param2:%s"), param…

docker搭建redis6.0(docker rundocker compose演示)

文章讲了&#xff1a;docker下搭建redis6.0.20遇到一些问题&#xff0c;以及解决后的最佳实践方案 文章实现了&#xff1a; docker run搭建redisdocker compose搭建redis 搭建一个redis’的过程中遇到很多问题&#xff0c;先简单说一下搭建的顺序 找一个redis.conf文件&…

spsr 的恢复出错,导致 thumb 指令集的 it 条件运行指令运行异常,清晰的调试思路帮助快速解决问题

记一次调试过程 这是一个在 arm 架构上的 RTOS 上的调试过程。问题现象为使用 thumb 指令集的 libgcc 库的情况下&#xff0c;浮点运算随机出错。经过一番追踪调试&#xff0c;逐步缩小问题范围&#xff0c;最后定位问题&#xff0c;成功解决。 场景 在某款的国产 RTOS 上&a…

RIP动态路由协议详解

目录 一&#xff1a;RIP协议的基本信息 二&#xff1a;RIP协议中的更新方式 三&#xff1a;RIP协议中的计时器 定时更新器&#xff08;UPDATE timer&#xff09; 无效定时器&#xff08;invalid Timer&#xff09; 垃圾收集定时器&#xff08;garbage collection timer&a…

【退役之重学 Java】初步认识 AQS

一、AQS 是什么 Abstract Queued Synchronizer &#xff0c;翻译过来就是“抽象的排好队的同步器”。 AQS 是一个用来构建锁和同步器的框架。是用来构建锁或者其他同步器组件的重量级基础框架及整个JUC体系的基石&#xff0c;通过内置的FIFO队列来完成线程获取资源的排队工作&…

【linux软件基础知识】如何使用 run_list 字段将任务放入就绪队列中

在给定的代码片段中,struct task_struct 表示内核中任务或进程的进程控制块 (PCB)。 run_list 字段的类型为 struct list_head,这表明它是链表实现的一部分。 run_list字段在Linux内核中常用来表示任务在调度队列中的位置,例如就绪队列或各种优先级队列。 init_task是一个…

第100+7步 ChatGPT文献复现:ARIMA-GRNN预测出血热

基于WIN10的64位系统演示 一、写在前面 这一次&#xff0c;我们来解读ARIMA-GRNN组合模型文章&#xff0c;也是老文章了&#xff1a; 《PLoS One》杂志的2015年一篇题目为《Comparison of Two Hybrid Models for Forecasting the Incidence of Hemorrhagic Fever with Renal…

树莓派、ubuntu低版本python3安装库

如果遇到树莓派中自带低版本python3&#xff0c;又不想额外去安装python3时&#xff0c;可能会遇到版本过低&#xff0c;无法安装库的情况&#xff0c;以下用我实际情况举例解决方案。 本次遇到的问题是树莓派低版本中&#xff0c;python3为3.7.3&#xff0c;需要安装numpy&am…

基于STC12C5A60S2系列1T 8051单片机实现一主单片机与一从单片机进行双向串口通信功能

基于STC12C5A60S2系列1T 8051单片机实现一主单片机与一从单片机进行双向串口通信功能 STC12C5A60S2系列1T 8051单片机管脚图STC12C5A60S2系列1T 8051单片机串口通信介绍STC12C5A60S2系列1T 8051单片机串口通信的结构基于STC12C5A60S2系列1T 8051单片机串口通信的特殊功能寄存器…

数据结构与算法===递归

文章目录 定义适用场景爬楼梯代码实现 小结 定义 递归(Recursion)是指函数的自身调用。 这个算法演变为了程序员之间的梗&#xff0c;所表达的意思近似于“套娃”&#xff0c;表示不断重复引用别人的话从而产生循环。 适用场景 这个应该很多的&#xff0c;像一些树的遍历&am…

Java | Leetcode Java题解之第84题柱状图中最大的矩形

题目&#xff1a; 题解&#xff1a; class Solution {public int largestRectangleArea(int[] heights) {int n heights.length;int[] left new int[n];int[] right new int[n];Arrays.fill(right, n);Deque<Integer> mono_stack new ArrayDeque<Integer>();f…

数据结构与算法学习笔记三---队列的顺序表示和实现(C语言)

目录 前言 1.顺序队列的描述 2.队列的顺序表示和实现 1.定义 2.初始化 3.销毁 4.清空 5.空队列 6.队列长度 7.获取队头 8.入队 9.出队 10.遍历队列 11.完整代码 前言 本篇博客介绍栈和队列的表示和实现。 1.顺序队列的描述 图1.顺序队列的描述 2.队列的顺序表示…