• 物联网操作系统HelloX应用编程指南


    HelloX操作系统应用编程指南

    HelloX应用开发概述

    可以通过三种方式,在HelloX操作系统基础上开发应用:

    1.        以内部命令方式实现应用,直接编译链接到HelloX的内核shell中。这时候应用代码的执行上下文,是shell线程的上下文。应用程序代码不会单独成为一个独立的线程;

    2.        以外部命令方式实现应用。直接编译链接到HelloX的内核中,通过shell来启动应用。这时候的应用,内核会创建一个独立的线程来承载;

    3.        以外部应用方式实现应用。应用代码单独编译链接,完成后存放在安装了HelloX操作系统的计算机存储设备中。在shell下,使用loadapp命令,加载可执行的应用模块。这种方式下,应用代码也是在一个独立的线程中执行的。需要注意的是,在这种方式下,应用代码只能调用HelloX SDK提供的API接口函数和C库函数。

    本文主要对第一种和第二种实现方式进行说明。

    以内部命令方式实现应用

    内部命令采用映射表的方式,使系统功能的添加和删除变得比较容易。比如要添加一个功能,只需要写一个处理函数,然后在HelloX的内部命令射表内添加一个项目即可。下面我们通过一个示例来说明如何往系统中添加新的功能。

    第一步:假设新增的功能命令为mycommand,首先编写一个功能函数,可以直接在SHELL.CPP文件中添加,也可以通过另外的模块实现,然后在SHELL.CPP中,包含实现的命令函数的头文件。假设mycommand命令的实现函数如下。

    VOID mycommand(LPSTR)

    {

        ChangeLine();

       PrintLine("Hello,World!");

        ChangeLine();

    }

    该函数的功能十分简单,打印出“Hello,World!”字符串,这也是大多数编程语音的一个入门示例。

    第二步:把该命令字符串和命令函数添加到内部命令列表中,并更改CMD_OBJ_NUM宏为原来的值加一,因为新增加了一个内部命令。代码如下(黑体部分是修改内容):

    //#define CMD_OBJ_NUM  21

    #defineCMD_OBJ_NUM  22

    __CMD_OBJ  CmdObj[CMD_OBJ_NUM] = {

             {"version"  ,   VerHandler},

             {"memory"   ,   MemHandler},

             {"sysinfo"  ,   SysInfoHandler},

             {"sysname"  ,   SysNameHandler},

             {"help"     ,   HlpHandler},

             {"cpuinfo"  ,   CpuHandler},

             {"support"  ,   SptHandler},

             {"runtime"  ,   RunTimeHandler},

             {"test"     ,   TestHandler},

             {"untest"   ,   UnTestHandler},

             {"memview"  ,   MemViewHandler},

             {"ktview"   ,   KtViewHandler},

             {"ioctrl"   ,   IoCtrlApp},

             {"sysdiag"  ,   SysDiagApp},

             {"loadapp"  ,   LoadappHandler},

             {"gui"      ,   GUIHandler},

             {"reboot"   ,   Reboot},

             {"poff"     ,   Poweroff},

             {"cls"      ,   ClsHandler},

        {“mycommand”,   mycommand}

    };

    第三步:重新编译连接(rebuild)整个操作系统核心,并重新制作引导盘引导系统。成功启动后,在命令行提示符下,输入mycommand并回车,就可以看到mycommand的输出了。

    需要注意的是,内部命令是直接在shell线程上下文中被调用的,没有自己独立的执行环境。因此只适合于实现较为简单的功能。

    以外部命令方式实现应用

    外部命令则会有独立的执行上下文,被内核以独立线程方式加载运行。因此,外部命令的功能非常强大,可以进一步以子命令的方式,实现更进一步的功能。比如,HelloX的网络诊断功能,就是以外部命令方式实现的。用户在shell界面下,输入network后回车,即可进入network诊断命令。这时候系统提示符会改变。在network命令下,输入help命令,可以看到该命令模式下所有可用的子命令,如下图:

    其中iflist,ping等就是network应用下的子命令。在执行子命令的时候,用户可以指定参数,HelloX会以一种统一的方式,把用户指定的参数,传递给命令处理函数。

    下面以network诊断外部命令的实现为例,说明外部命令的实现方式。

    第一步:编写外部命令的入口处理函数。

    以network命令为例,要编写类似下列形式的处理函数:

    DWORD networkEntry(LPVOID p)

    {

             return Shell_Msg_Loop(NETWORK_PROMPT_STR,CommandParser,QueryCmdName);

    }

    具体的处理函数的实现,就是开发者大显神通的地方。比如要实现子命令,则需要定义一些子命令映射列表等信息。在上面的实现中,是一个消息处理循环,根据用户的输入,来调用特定的子命令。

    注意,外部命令下的子命令,既可以直接在外部命令的线程上下文中执行,也可以单独创建执行线程,取决于开发者的判断。

    需要注意的是,外部命令的字命令处理函数,必须以下列格式来定义:

    static DWORD ping(__CMD_PARA_OBJ* lpCmdObj)

    {      

             __PING_PARAM     PingParam;

             ip_addr_t        ipAddr;

             int              count      = 3;   //Ping counter.

             int              size       = 64;  //Ping packet size.

             BYTE             index      = 1;

             DWORD            dwRetVal   = SHELL_CMD_PARSER_FAILED;

             __CMD_PARA_OBJ*  pCurCmdObj = lpCmdObj;

            

             if(pCurCmdObj->byParameterNum<= 1)

             {

                       return dwRetVal;

             }

             while(index <lpCmdObj->byParameterNum)

             {

                       if(strcmp(pCurCmdObj->Parameter[index],"/c")== 0)

                       {

                                index++;

                                if(index>= lpCmdObj->byParameterNum)

                                {

                                         break;

                                }

                                count    = atoi(pCurCmdObj->Parameter[index]);

                       }

                       elseif(strcmp(pCurCmdObj->Parameter[index],"/l") == 0)

                       {

                                index++;

                                if(index>= lpCmdObj->byParameterNum)

                                {

                                         break;

                                }

                                size   = atoi(pCurCmdObj->Parameter[index]);

                       }

                       else

                       {

                                ipAddr.addr= inet_addr(pCurCmdObj->Parameter[index]);

                       }

                       index ++;

             }

             if(ipAddr.addr != 0)

             {

                       dwRetVal    = SHELL_CMD_PARSER_SUCCESS;

             }

             PingParam.count      = count;

             PingParam.targetAddr =ipAddr;

             PingParam.size       = size;

             //Call ping entry routine.

             ping_Entry((void*)&PingParam);

             return dwRetVal;

    }

    子命令处理函数必须返回一个DWORD类型的值,用来表示子命令的执行情况,比如成功或者是失败。同时,子命令处理函数的参数,也必须是__CMD_PARA_OBJ* 类型。这是个内部定义的参数传递数据结构,如下:

    typedef struct tag__CMD_PARA_OBJ

    {

             BYTE        byParameterNum;         //How many parameters  followed.

             WORD        wReserved;

             CHAR*       Parameter[CMD_PARAMETER_COUNT];

    }__CMD_PARA_OBJ;

    byParameterNum指明了这个结构体中包含的参数个数,而Parameter则是一个字符串数组,包含了每个字符串参数的首地址。这与标准的C入口函数main(intargc,char* argv[])的参数是一致的。其中byParameterNum与argc对应,而Parameter则与argv数组对应。需要注意的是,数组中的第一个参数,就是子命令字符串本身。这与C的argv数组中,第一个是应用程序文件名字符串的情况一致。

    子命令函数就可以通过分析__CMD_PARA_OBJ对象,来获取每个参数。

    第二步:在外部命令数组中,增加入口函数信息。

    外部命令数组在kernel/shell/extcmd.c文件中,在这个数组中增加一项,如下:

    __EXTERNAL_COMMAND ExtCmdArray[] = {

             {"fs",NULL,FALSE,fsEntry},

             {"fdisk",NULL,FALSE,fdiskEntry},

             {"hedit",NULL,FALSE,heditEntry},

             {"fibonacci",NULL,FALSE,Fibonacci},

             {"hypertrm",NULL,FALSE,Hypertrm},

             {"hyptrm2",NULL,FALSE,Hyptrm2},

    #if defined(__CFG_NET_IPv4) || defined(__CFG_NET_IPv6)

             {"network",NULL,FALSE,networkEntry},

    #endif

             //Add your externalcommand/application entry here.

             //{"yourcmd",NULL,FALSE,cmdentry},

             //The last entry of thisarray must be the following one,

             //to indicate theterminator of this array.

             {NULL,NULL,FALSE,NULL}

    };

    数组元素的第一个参数,就是定义的外部命令字符串。用户在shell下,输入该字符串,shell就会以这个字符串为索引,搜索这个数组,找到对应的执行函数,创建一个内核线程并执行。数组元素中的最后一个参数,就是对应的外部命令入口函数。数组元素的第二和第三个参数,主要是指明了外部命令的入口参数,以及是否已阻塞方式执行。如果设置为TRUE,则以非阻塞方式执行,也就是与shell一起并行执行。否则的话,shell会阻塞,等待外部命令执行完毕后,再继续执行。一般设置为FALSE,这样可以确保shell能够及时的释放掉相关资源。

    第三步:在帮助数组中增加一行,确保外部命令能够在help输出中可见。

    在shell下,用户输入help命令,可以列出系统中所有可用的内部和外部命令。为了确保新增加的外部命令在help命令中可见,需要在下列数组(kernel/shell/shell1.c)中增加相关的帮助描述和输出信息:

    //Handler for help command.

    DWORD HlpHandler(__CMD_PARA_OBJ* pCmdParaObj)           //Command 'help' 's handler.

    {

             LPSTR strHelpTitle   = "   The following commands are available currently:";

             LPSTR strHelpVer     = "   version      : Print out theversion information.";

             LPSTR strHelpMem     = "   memory       : Print out currentversion's memory layout.";

             LPSTR strHelpSysInfo ="    sysinfo      : Print out the system context.";

             LPSTR strSysName     = "   sysname      : Change the systemhost name.";

             LPSTR strHelpHelp    = "   help         : Print out thisscreen.";

             LPSTR strSupport     = "   support      : Print out technicalsupport information.";

             LPSTR strTime        = "    time        : Show system date and time.";

             LPSTR strRunTime     = "   runtime      : Display the totalrun time since last reboot.";

             LPSTR strIoCtrlApp   ="    ioctrl       : Start IO control application.";

             LPSTR strSysDiagApp  = "   sysdiag      : System or hardwarediag application.";

             LPSTR strFsApp       = "    fs          : File system operating application.";

             LPSTR strFdiskApp    = "   fdisk        : Hard disk operating application.";

             LPSTR strNetApp      = "    network     : Network diagnostic application.";

             LPSTR strLoadappApp  = "   loadapp      : Load applicationmodule and execute it.";

             LPSTR strGUIApp      = "    gui         : Load GUI module and enter GUI mode.";

    #ifdef __CFG_APP_JVM

             LPSTR strJvmApp      = "    jvm         : Start Java VM to run Java Application.";

    #endif  //__CFG_APP_JVM

             LPSTR strReboot      = "    reboot      : Reboot the system.";

             LPSTR strCls         = "    cls         : Clear the whole screen.";

             PrintLine(strHelpTitle);              //Print out the help informationline by line.

             PrintLine(strHelpVer);

             PrintLine(strHelpMem);

             PrintLine(strHelpSysInfo);

             PrintLine(strSysName);

             PrintLine(strHelpHelp);

             PrintLine(strSupport);

             PrintLine(strTime);

             PrintLine(strRunTime);

             PrintLine(strIoCtrlApp);

             PrintLine(strSysDiagApp);

             PrintLine(strFsApp);

             PrintLine(strNetApp);

             PrintLine(strFdiskApp);

             PrintLine(strLoadappApp);

             PrintLine(strGUIApp);

    #ifdef __CFG_APP_JVM

             PrintLine(strJvmApp);

    #endif //__CFG_APP_JVM

             PrintLine(strReboot);

             PrintLine(strCls);

             return S_OK;

    }

    需要注意的是,不仅仅要增加一个LPSTR(char*)类型的字符串定义,还要在下面增加对应的PrintLine输出,否则不会有信息书出来。

    完成上述三个步骤之后,重新编译HelloX内核,然后用最新的内核引导计算机。进入shell后,执行help命令,应该可以看到新增加的外部命令了。输入对应的外部命令字符串,即可看到外部命令的执行结果。

    实际上,外部命令的复杂之处,主要还是在于如何处理用户输入,以及如何根据用户的输入,调用对应的子命令处理函数。HelloX实现了很多外部命令,比如本文讲到的network命令,系统诊断sysdiag命令,输入输出控制ioctrl命令,以及文件系统操作命令fs等。开发者可以在这些外部实现的基础上,利用已有的框架,修改特定的部分即可。比如,对于外部命令的入口函数,可以直接在现有的基础上,修改一下函数名。对于子命令处理函数,可以根据需要,进行修改或定义。完成后,要增加到本地子命令映射数组中。比如network命令的子命令映射数组如下:

    static struct __NETWORK_CMD_MAP{

             LPSTR                lpszCommand;

             DWORD               (*CommandHandler)(__CMD_PARA_OBJ*);

             LPSTR                lpszHelpInfo;

    }SysDiagCmdMap[] = {

             {"iflist",     iflist,   "  iflist   : Show all network interface(s) insystem."},

             {"ping",       ping,      " ping     : Check a specifiedhost's reachbility."},

             {"route",      route,    "  route    : List all route entry(ies) insystem."},

             {"exit",       _exit,      " exit     : Exit theapplication."},

             {"help",       help,      " help     : Print out thisscreen."},

             {"showint",    showint,  "  showint  : Display ethernet interface's statisticsinformation."},

             {"assoc",      assoc,    "  assoc    : Associate to a specified WiFiSSID."},

             {"scan",       scan,      " scan     : Scan WiFi networks andshow result."},

             {"setif",      setif,    "  setif    : Set IP configurations to a giveninterface."},

             {NULL,                 NULL,      NULL}

    };

    数组元素的第一个参数,就是子命令字符串,第二个参数,是该子命令的处理函数。最后一个字符串信息,则是该子命令的帮助信息。在外部命令提示符下,输入help,就会显示出所有可用的子命令帮助信息。

    开发者可以在此基础上,根据自己的实现,修改或删除特定的映射表项即可。

    需要注意的是,为了确保整体代码的整洁,建议外部命令的组织,遵循下列原则:

    1.   所有新增外部命令的代码,放在一个新创建的代码文件中,不要与现有的shell源文件放在一起;

    2.   新增加的外部命令源文件,放在shell目录下。

  • 相关阅读:
    zbb20181207 springboot @ConfigurationProperties使用
    zbb20181206 logback,lombok 默认日志logback配置解析
    Spring Boot (8) 全局异常处理
    Spring Boot (7) JdbcTemplate访问数据库
    Spring Boot (6) Spring Data JPA
    Spring Boot (4) 静态页面和Thymeleaf模板
    Spring Boot (3) 热部署devtools
    Spring Boot (2) Restful风格接口
    Spring Boot (1) 构建第一个Spring Boot工程
    idea使用maven搭建ssm框架实现登陆商品增删改查
  • 原文地址:https://www.cnblogs.com/fengju/p/6174186.html
Copyright © 2020-2023  润新知