嵌入式操作系统
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

3.4 外部命令的处理过程

之所以有外部命令和内部命令之分,一个原因是为了编程上的方便。在当前的实现中,所有内部命令都是在SHELL所在的源文件内实现的,而外部命令,则统一以EXTCMD.CPP为接口。实现的时候,用户可以通过另外的模块实现功能部分,然后只需要在EXTCMD.CPP文件当中做一个简单的修改即可。另外的一个因素是所有内部命令都是以函数的形式实现的,即内部命令的运行上下文与Shell共享,但外部命令的实现则是通过创建单独的核心线程实现的,有自己的线程上下文。

为了实现外部命令,特定义如下的数据结构。

  __BEGIN_DEFINE_OBJECT(__EXTERNAL_COMMAND)
    LPSTR           lpszCmdName;
    LPSTR           lpszHelpInfo;
    BOOL            bBackground;
    DWORD           (*ExtCmdHandler)(LPVOID);
__END_DEFINE_OBJECT()

其中,lpszCmdName是外部命令的命令字符串,lpszHelpInfo是外部命令的帮助信息,通过执行help命令,可把外部命令的相关帮助信息打印出来。bBackground变量是一个指示变量,告诉操作系统,该外部命令是在后台执行还是在前台执行。若是在后台执行,则操作系统创建外部命令的执行线程,然后就返回用户界面(Shell线程),这时候,用户界面仍然正常响应用户需求。若是在前台执行,则操作系统会创建外部命令的执行线程后,将一直等待外部命令执行结束,然后才返回Shell,这个过程,用户只能与外部命令提供的用户接口进行交互。ExtCmdHandler则是具体的外部命令入口点,可以看出,该函数的原型是与线程入口点的函数原型相匹配的。

定义如下数组,来管理所有的外部命令。

__EXTERNAL_COMMAND ExtCmdArray[ ]={
    {"extcmd1", "The first external command.", TRUE,ExtCmd1},
    {NULL,NULL,FALSE,NULL}
};

在实现外部命令的时候,只需要在上述数组中加入对应的内容,就可以被系统识别。这时候,在命令提示符下,只需输入外部命令字符串,外部命令就可以得到执行。另外,通过执行help命令,可以打印出外部命令的帮助信息。

对于外部命令的处理,是由DoExternalCmd函数完成的,该函数被DoCommand函数调用(参见上面内部命令实现的描述),代码如下。

BOOL  DoExternalCmd(LPSTR lpszCmd)
{
    DWORD wIndex   =0x0000;
      DWORD i        =0;
      LPSTR lpszParam =NULL;
      __KERNEL_THREAD_OBJECT* lpExtCmd=NULL;
    BOOL bResult   =FALSE;  //If find the correct command object,then
                              //This flag set to TRUE.
    BYTE tmpBuffer[36];
    while((' ' !=lpszCmd[wIndex]) && lpszCmd[wIndex] && (wIndex < 32))
    {
        tmpBuffer[wIndex]=lpszCmd[wIndex];
        wIndex++;
    }
    tmpBuffer[wIndex]=0;
    while(ExtCmdArray[i].lpszCmdName)
    {
        if(StrCmp(&tmpBuffer[0],ExtCmdArray[i].lpszCmdName))
//Find the correct external command entry.
        {
              //
              //Handle external command here.
              //
              if(tmpBuffer[wIndex+1])  //Have parameters.
              {
                lpszParam=
(LPSTR)KMemAlloc(StrLen(&tmp Buffer[wIndex+1]+1),KMEM_SIZE_TYPE_ANY);
                if(NULL==lpszParam) //Can not allocate memory.
                {
                    bResult=FALSE;
                    break;
                }
                StrCpy(lpszParam,&tmpBuffer[wIndex+1]);
              }
              lpExtCmd=KernelThread Manager .CreateKernelThread(
                          (__COMMON_OBJECT*) &Kernel Thread Manager,
                          0L,
                          KERNEL_THREAD_STATUS_READY,
                          PRIORITY_LEVEL_NORMAL,
                          ExtCmdArray[i].ExtCmdHandler,
                          lpszParam,
                          NULL);
              if(NULL==lpExtCmd) //Can not create thread.
              {
                bResult=FALSE;
                break;
              }
              if(!ExtCmdArray[i].bBackground)
                                    //Should run in foreground.
              {
                DeviceInputManager.SetFocusThread(
(__COMMON_OBJECT *)&Device InputManager,
                                    (__COMMON_ OBJECT*) lpExtCmd);
                                    //Set focus.
  lpExtCmd->WaitForThisObject ((__COMMON_ OBJECT*)lpExtCmd); //Blocking.
                        }
            bResult=TRUE;     //Set the flag.
            break;
        }
    }
    return bResult;
}

与内部命令处理类似,首先从命令行中分离出具体的外部命令,然后根据外部命令字符串匹配外部命令列表,若找不到匹配的项,则返回FALSE,从而导致DefaultHandler被调用(参考DoCommand函数)。若匹配成功,则进一步判断,用户是否输入了作用于该外部命令的参数。若有额外的参数,则该函数申请一块内存,并把额外的参数复制到申请的内存中。然后调用CreateKernelThread函数,创建一个线程,线程的入口点就是用户提供的外部命令处理函数。需要注意的是,在存在额外参数的情况下,由于DoExternalCmd函数申请了内存,然后把该内存传递到了新创建的函数,因此这块内存的释放应该是外部命令处理函数的工作。若外部命令处理函数不释放参数占用的内存,则可能会引起内存泄漏。

在完成外部命令执行线程的创建后,会进一步判断该外部命令是否需要在后台执行。若是后台执行程序,则DoExternalCmd函数直接返回,若是非后台执行程序,则DoExternalCmd函数需要等待外部命令线程的执行,进入阻塞状态。这种情况下,仍把当前的输入焦点设置为外部命令执行线程,以便外部命令执行线程能够接受用户输入。