helloworld程序是如何被調(diào)用的,相信很多沒有經(jīng)驗的人對此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個問題。
創(chuàng)新互聯(lián)建站主要從事成都做網(wǎng)站、網(wǎng)站設計、網(wǎng)頁設計、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務。立足成都服務加格達奇,10多年網(wǎng)站建設經(jīng)驗,價格優(yōu)惠、服務專業(yè),歡迎來電咨詢建站服務:028-86922220
分析 helloworld程序是如何被調(diào)用,SYS_RUN做什么事情
相信大家都已經(jīng)在鴻蒙系統(tǒng)上實現(xiàn)了自己的第一個helloworld程序了。
代碼很簡單,編譯燒錄后,我們就可以看到串口有打印 [DEMO] Hello world.
但是 HelloWorld 函數(shù)是在何時被調(diào)用的呢?SYS_RUN 又是干嘛的呢?
我們來看下。
1. 啟動流程
首先,我們需要分析一下Hi3861的啟動流程。目前Hi3861 使用的是liteOS-M內(nèi)核,相關(guān)源碼廠家沒有提供,不過也不妨礙我們。經(jīng)過我一番查找,可以知道hi3861啟動內(nèi)后,第一個入口函數(shù)是 app_main函數(shù)。
(vendor\hisi\hi3861\hi3861\app\wifiiot_app\src\app_main.c)
大家可以打開,看到app_main函數(shù)的內(nèi)容,如下,當然我這里只是簡版的,我刪除了很多初始化的函數(shù),只保留最終要的。
hi_void app_main(hi_void) { //打印sdk版本 const hi_char* sdk_ver = hi_get_sdk_version(); printf("sdk ver:%s\r\n", sdk_ver); //串口、IO初始化等 peripheral_init(); //wifi初始化 ret = hi_wifi_init(APP_INIT_VAP_NUM, APP_INIT_USR_NUM); //鴻蒙系統(tǒng)初始化 HOS_SystemInit(); }
我們可以看到其實app_main啟動后做了很多工作,包括io初始化、wifi初始,最后調(diào)用了HOS_SystemInit(); 進行鴻蒙系統(tǒng)最后的初始化。
那我們看下 HOS_SystemInit(); 做了什么動作吧。
打開源碼 base\startup\services\bootstrap_lite\source\system_init.c
可以看到函數(shù)內(nèi)容如下:
void HOS_SystemInit(void) { MODULE_INIT(bsp); MODULE_INIT(device); MODULE_INIT(core); SYS_INIT(service); SYS_INIT(feature); MODULE_INIT(run); SAMGR_Bootstrap(); }
看起來好像在調(diào)用某些模塊,仔細看,其中有一個是 MODULE_INIT(run); 。顧名思義,好像在初始化或者調(diào)用 一個 run 模塊。那run模塊又是什么呢?我們看下標題的 SYS_RUN(HelloWorld)。 是不是可以猜測其實MODULE_INIT(run); 就是調(diào)用了 HelloWorld 呢?
哈哈哈~其實還真是。大家如果加打印信息,可以看到如下打印。
../../base/startup/services/bootstrap_lite/source/system_init.c 38 ../../applications/sample/wifi-iot/app/my_first_app/hello_world.c 9 [DEMO] Hello world. ../../base/startup/services/bootstrap_lite/source/system_init.c 40
仔細看我加的打印語句,確實是在 38 行執(zhí)行 MODULE_INIT(run); 后才打印 [DEMO] Hello world.
所以跟我們猜測的一樣。當然沒完,我們得分析為啥 是這樣。
2. 鏈接
我們看下 MODULE_INIT(run); 做了什么。事實上,它只是一個宏。
#define MODULE_INIT(name) \ do { \ MODULE_CALL(name, 0); \ } while (0)
而 MODULE_CALL(name, 0); 又可以展開:當然里面的if 語句的打印是我后面加的
我們可以看到 它其實是定義了一個 InitCall 指針,然后指針是這個:
(MODULE_BEGIN(name, step))
而 MODULE_BEGIN 宏其實展開后如下:
#define MODULE_NAME(name, step) ".zinitcall.">
我這里再幫大家展開,其實".zinitcall." #name #step ".init" 最后 就是 .zinitcall.run2.init
它其實是一種寫法,就是說我們代碼編譯的時候,代碼里面有一段地址比較特殊,它的名字是 .zinitcall.run2.init ,也就是說 InitCall 指針 指向的是 .zinitcall.run2.init 代碼段的地址。
畫個圖:
綠色的是.zinitcall.run2.init 代碼段,里面存放著函數(shù)指針。
好了,到這里大家應該都明白了吧,繼續(xù)看這個圖,其實 這里只不過是把這個代碼段里面的所有函數(shù)指針都取出來,然后執(zhí)行一下函數(shù)指針指向的函數(shù)。
到了這里就剩下最后一個問題了: 怎么讓它指向 HelloWorld 函數(shù)。
這里其實就是 SYS_RUN的功勞了。
我們也來看SYS_RUN做了什么,其它也是一個宏,展開過程如下:
我們可以看到,其實SYS_RUN(HelloWorld) 其實最終結(jié)果就是:
static const InitCall USED_ATTR __zinitcall_##layer##_##func \ __attribute__((section(".zinitcall.">
看起來很復雜,我們不乏拆解一下:
我們先不看紅色字體部分,那么結(jié)果就是:
static const InitCall = HelloWorld
是不是很簡單,其實就是定義了一個全局變量(函數(shù)指針),它指向 HelloWorld 。
那紅色字體是做什么用呢?它其實就是告訴編譯,我這個變量(static const InitCall 變量),很特殊,編譯的時候給我編譯在 .zinitcall.run2.init 段。
3. 忠告
這里有兩個忠告:
1、請不要直接在SYS_RUN()定義的入口函數(shù)直接寫 while(1)
——這個很簡單理解了,因為系統(tǒng)啟動后,app_main會調(diào)用到 我們定義的SYS_RUN()定義的入口函數(shù),比如HelloWorld。 如果我們在 HelloWorld 函數(shù)中寫了while(1) 就會導致 app_main 后續(xù)的代碼得不到執(zhí)行,肯定有問題。
2、SYS_RUN()定義的入口函數(shù)創(chuàng)建的線程,請一定要有sleep動作。
為了解決第一個問題,我們很自然地想到,可以在 SYS_RUN()定義的入口函數(shù) 創(chuàng)建線程,這樣就可以while(1)了。哈哈,其實也是有問題,因為 app_main 本身也是一個任務,如果我們自己創(chuàng)建地任務優(yōu)先級特別高,就會導致 app_main 任務不會被執(zhí)行,還是有問題。所以要有sleep,確保 app_main 后續(xù)地代碼能順利執(zhí)行下去。
看完上述內(nèi)容,你們掌握helloworld程序是如何被調(diào)用的的方法了嗎?如果還想學到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!
當前名稱:helloworld程序是如何被調(diào)用的
文章鏈接:http://www.ekvhdxd.cn/article2/jcgdic.html
成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供標簽優(yōu)化、App開發(fā)、網(wǎng)站營銷、外貿(mào)建站、企業(yè)建站、網(wǎng)站改版
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)