v72.01 鸿蒙内核源码分析(Shell解析) | 应用窥伺内核的窗口 | 百篇博客分析OpenHarmony源码 (2)

需在链接选项中添加链接该新增命令项参数,具体在liteos_tables_ldflags.mk文件的LITEOS_TABLES_LDFLAGS项下添加-uls_shellcmd。至于SHELLCMD_ENTRY是如何实现的在链接阶段的注册,请自行翻看(内联汇编篇),有详细说明实现细节.

动态命令方式,运行时处理
动态注册命令方式一般用在用户命令注册,具体实现代码如下:

osCmdReg(CMD_TYPE_EX, "ls", XARGS, (CMD_CBK_FUNC)osShellCmdLs) { // .... //5.正式创建命令,挂入链表 return OsCmdItemCreate(cmdType, cmdKey, paraNum, cmdProc);//不存在就注册命令 } //创建一个命令项,例如 chmod STATIC UINT32 OsCmdItemCreate(CmdType cmdType, const CHAR *cmdKey, UINT32 paraNum, CmdCallBackFunc cmdProc) { CmdItem *cmdItem = NULL; CmdItemNode *cmdItemNode = NULL; //1.构造命令节点过程 cmdItem = (CmdItem *)LOS_MemAlloc(m_aucSysMem0, sizeof(CmdItem)); if (cmdItem == NULL) { return OS_ERRNO_SHELL_CMDREG_MEMALLOC_ERROR; } (VOID)memset_s(cmdItem, sizeof(CmdItem), '\0', sizeof(CmdItem)); cmdItemNode = (CmdItemNode *)LOS_MemAlloc(m_aucSysMem0, sizeof(CmdItemNode)); if (cmdItemNode == NULL) { (VOID)LOS_MemFree(m_aucSysMem0, cmdItem); return OS_ERRNO_SHELL_CMDREG_MEMALLOC_ERROR; } (VOID)memset_s(cmdItemNode, sizeof(CmdItemNode), '\0', sizeof(CmdItemNode)); cmdItemNode->cmd = cmdItem; //命令项 cmdItemNode->cmd->cmdHook = cmdProc;//回调函数 osShellCmdLs cmdItemNode->cmd->paraNum = paraNum;//`777`,'/home' cmdItemNode->cmd->cmdType = cmdType;//关键字类型 cmdItemNode->cmd->cmdKey = cmdKey; //`chmod` //2.完成构造后挂入全局链表 (VOID)LOS_MuxLock(&g_cmdInfo.muxLock, LOS_WAIT_FOREVER); OsCmdAscendingInsert(cmdItemNode);//按升序方式插入 g_cmdInfo.listNum++;//命令总数增加 (VOID)LOS_MuxUnlock(&g_cmdInfo.muxLock); return LOS_OK; }

第二步 解析 | ShellTask //shell 服务端任务初始化,这个任务负责解析和执行命令 LITE_OS_SEC_TEXT_MINOR UINT32 ShellTaskInit(ShellCB *shellCB) { CHAR *name = NULL; TSK_INIT_PARAM_S initParam = {0}; //输入Shell命令的两种方式 if (shellCB->consoleID == CONSOLE_SERIAL) { //通过串口工具 name = SERIAL_SHELL_TASK_NAME; } else if (shellCB->consoleID == CONSOLE_TELNET) {//通过远程工具 name = TELNET_SHELL_TASK_NAME; } else { return LOS_NOK; } initParam.pfnTaskEntry = (TSK_ENTRY_FUNC)ShellTask;//任务入口函数,主要是解析shell命令 initParam.usTaskPrio = 9; /* 9:shell task priority */ initParam.auwArgs[0] = (UINTPTR)shellCB; initParam.uwStackSize = 0x3000; initParam.pcName = name; initParam.uwResved = LOS_TASK_STATUS_DETACHED; (VOID)LOS_EventInit(&shellCB->shellEvent);//初始化事件,以事件方式通知任务解析命令 return LOS_TaskCreate(&shellCB->shellTaskHandle, &initParam);//创建任务 } LITE_OS_SEC_TEXT_MINOR UINT32 ShellTask(UINTPTR param1, UINTPTR param2, UINTPTR param3, UINTPTR param4) { UINT32 ret; ShellCB *shellCB = (ShellCB *)param1; (VOID)param2; (VOID)param3; (VOID)param4; while (1) { PRINTK("\nOHOS # ");//读取shell 输入事件 例如: cat weharmony.net 命令 ret = LOS_EventRead(&shellCB->shellEvent, 0xFFF, LOS_WAITMODE_OR | LOS_WAITMODE_CLR, LOS_WAIT_FOREVER); if (ret == SHELL_CMD_PARSE_EVENT) {//获得解析命令事件 ShellCmdProcess(shellCB);//处理命令 } else if (ret == CONSOLE_SHELL_KEY_EVENT) {//退出shell事件 break; } } OsShellKeyDeInit((CmdKeyLink *)shellCB->cmdKeyLink);// OsShellKeyDeInit((CmdKeyLink *)shellCB->cmdHistoryKeyLink); (VOID)LOS_EventDestroy(&shellCB->shellEvent);//注销事件 (VOID)LOS_MemFree((VOID *)m_aucSysMem0, shellCB);//释放shell控制块 return 0; }

解读

任务优先级和 客户端任务 一样同为 9

指定内核栈大小为0x3000 = 12K ,因任务负责命令的解析和执行,所以需要更大的内核空间.

任务的入口函数ShellTask,一个死循环在以LOS_WAIT_FOREVER方式死等事件发生.

SHELL_CMD_PARSE_EVENT 通知开始解析事件,该事件由 客户端任务ShellEntry检测到回车键时发出.

STATIC VOID ShellNotify(ShellCB *shellCB) { (VOID)LOS_EventWrite(&shellCB->shellEvent, SHELL_CMD_PARSE_EVENT); }

CONSOLE_SHELL_KEY_EVENT 收到 exit命令时将发出该事件,退出shell回收资源
鸿蒙内核是如何管理和使用事件的请自行翻看(事件控制篇)

层层跟进ShellCmdProcess,解析出命令项和参数内容,最终跑到OsCmdExec中遍历 已注册的命令表,找出命令对应的函数完成回调.

LITE_OS_SEC_TEXT_MINOR UINT32 OsCmdExec(CmdParsed *cmdParsed, CHAR *cmdStr) { UINT32 ret; CmdCallBackFunc cmdHook = NULL; CmdItemNode *curCmdItem = NULL; UINT32 i; const CHAR *cmdKey = NULL; if ((cmdParsed == NULL) || (cmdStr == NULL) || (strlen(cmdStr) == 0)) { return (UINT32)OS_ERROR; } ret = OsCmdParse(cmdStr, cmdParsed);//解析出命令关键字,参数 if (ret != LOS_OK) { goto OUT; } //遍历命令注册全局链表 LOS_DL_LIST_FOR_EACH_ENTRY(curCmdItem, &(g_cmdInfo.cmdList.list), CmdItemNode, list) { cmdKey = curCmdItem->cmd->cmdKey; if ((cmdParsed->cmdType == curCmdItem->cmd->cmdType) && (strlen(cmdKey) == strlen(cmdParsed->cmdKeyword)) && (strncmp(cmdKey, (CHAR *)(cmdParsed->cmdKeyword), strlen(cmdKey)) == 0)) {//找到命令的回调函数 例如: ls <-> osShellCmdLs cmdHook = curCmdItem->cmd->cmdHook; break; } } ret = OS_ERROR; if (cmdHook != NULL) {//执行命令,即回调函数 ret = (cmdHook)(cmdParsed->paramCnt, (const CHAR **)cmdParsed->paramArray); } OUT: for (i = 0; i < cmdParsed->paramCnt; i++) {//无效的命令要释放掉保存参数的内存 if (cmdParsed->paramArray[i] != NULL) { (VOID)LOS_MemFree(m_aucSysMem0, cmdParsed->paramArray[i]); cmdParsed->paramArray[i] = NULL; } } return (UINT32)ret; }

第三步 | 执行

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zwdsxw.html