需在链接选项中添加链接该新增命令项参数,具体在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;
}
第三步 | 执行