物聯網操作系統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目錄下。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章