Hi3861_WiFi IoT工程:理解啟動恢復(fù)子系統(tǒng)
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
Hi3861_WiFiIoT工程的一點理解
目錄
- 1.關(guān)于工程本身
- 2.ohos_bundles
- 3.工程的目錄結(jié)構(gòu)
- 4.理解IoT外設(shè)控制模塊
- 4.1 BUILD.gn 的展開
- 4.2 led_example.c 的展開
- 4.3 IoT外設(shè)控制模塊的整體理解
- 5.理解啟動恢復(fù)子系統(tǒng)
- 5.1 #A分析SYS_INIT(service)
- 5.2 #B分析MODULE_INIT(run)
- 5.3 #C分析SAMGR_Bootstrap()
- X.總結(jié):
更新記錄:
- 說明:本文是 "Hi3861_WiFi IoT工程的一點理解" 的新增章節(jié),版本升級到v1.6.
- 前面章節(jié)見:
- Hi3861_WiFi IoT工程的一點理解v1.0
- Hi3861_WiFi IoT工程:理解IoT外設(shè)控制模塊
5.理解啟動恢復(fù)子系統(tǒng)
這是一個非常重要的子系統(tǒng),我在之前《鴻蒙系統(tǒng)的啟動流程》一文中做過一些簡單的分析,建議先去看一下《鴻蒙系統(tǒng)的啟動流程v3.0》Part 1/2的3/4章節(jié)。這里就先到鴻蒙系統(tǒng)來整體看一下它具體是怎么回事,然后再回到本工程來對比看hpm的裁剪給我們留下了什么。
仍然是先看官方readme 和重新整理目錄結(jié)構(gòu)。
- 啟動恢復(fù)子系統(tǒng)負責(zé)從內(nèi)核啟動之后到應(yīng)用啟動之前的系統(tǒng)關(guān)鍵服務(wù)進程的啟動過程以及設(shè)備恢復(fù)出廠設(shè)置的功能。涉及以下組件:
- init啟動引導(dǎo)組件
- init啟動引導(dǎo)組件對應(yīng)的進程為init進程,是內(nèi)核完成初始化后啟動的第一個用戶態(tài)進程。init進程啟動之后,讀取init.cfg配置文 件,根據(jù)解析結(jié)果,執(zhí)行相應(yīng)命令并依次啟動各關(guān)鍵系統(tǒng)服務(wù)進程,在啟動系統(tǒng)服務(wù)進程的同時設(shè)置其對應(yīng)權(quán)限。
- appspawn應(yīng)用孵化組件
- 負責(zé)接收用戶程序框架的命令孵化應(yīng)用進程,設(shè)置新進程的權(quán)限,并調(diào)用應(yīng)用程序框架的入口函數(shù)。
- bootstrap服務(wù)啟動組件
- 提供了各服務(wù)和功能的啟動入口標識。在SAMGR啟動時,會調(diào)用boostrap標識的入口函數(shù),并啟動系統(tǒng)服務(wù)。
- syspara系統(tǒng)屬性組件
- 系統(tǒng)屬性組件,根據(jù)HarmonyOS產(chǎn)品兼容性規(guī)范提供獲取設(shè)備信息的接口,如:產(chǎn)品名、品牌名、廠家名等,同時提供設(shè)置/讀 取系統(tǒng)屬性的接口。
- startup啟動組件
- 負責(zé)提供大型系統(tǒng)(參考內(nèi)存≥1GB)獲取與設(shè)置操作系統(tǒng)相關(guān)的系統(tǒng)屬性。
- 大型系統(tǒng)支持的系統(tǒng)屬性包括:設(shè)備信息如設(shè)備類型、產(chǎn)品名稱等,系統(tǒng)信息如系統(tǒng)版本、API版本等默認系統(tǒng)屬性。
兩相比較就可以看到Hi3861工程相對于完整系統(tǒng)裁剪掉了appspawn_lite 和 init_lite兩個組件(先灰化掉了),因為啟動方式/流程上有比較大的差別,裁掉init_lite其實很容易理解,但為什么裁掉appspawn_lite我還沒仔細研究。
這里就只分析Hi3861的bootstrap_lite,至于syspara_lite比較簡單,看官方文檔照著上表右邊的調(diào)用順序就可以獲取屬性信息了。appspawn_lite 和 init_lite兩個組件,待我把相關(guān)細節(jié)搞清楚了,再完善到《鴻蒙系統(tǒng)的啟動流程》的更新版本中,或者單獨寫一個理解總結(jié)出來。
下面的文字,其實也算是《鴻蒙系統(tǒng)的啟動流程v3.0》Part 2的“4.第四階段:鴻蒙系統(tǒng)框架層的啟動”的完整分析版本。
官方readme 對bootstrap服務(wù)啟動組件的描述就兩句話,該怎么理解:
“提供了各服務(wù)和功能的啟動入口標識。” 就是指在//base/startup/services/bootstrap_lite/source/core_main.h 頭文件中定義的宏:SYS_INIT(name)和MODULE_INIT(name)。
- 【在這里又要強烈推薦去看:
- 連志安老師的《分析 helloworld程序是如何被調(diào)用,SYS_RUN做什么事情》
- 唐佐林老師的《SYS_RUN()和MODULE_INIT()之間的那些事》
- 這兩篇文章了?!?/li>
“在SAMGR啟動時,會調(diào)用boostrap標識的入口函數(shù),并啟動系統(tǒng)服務(wù)。”就是指在system_init.c文件中的HOS_SystemInit()函數(shù)調(diào)用 SAMGR_Bootstrap(); 去啟動系統(tǒng)服務(wù)了。什么是“boostrap標識的入口函數(shù)”,我們在下面會解釋。
- void HOS_SystemInit(void)
- {
- MODULE_INIT(bsp);
- MODULE_INIT(device);
- MODULE_INIT(core);
- SYS_INIT(service); //#A
- SYS_INIT(feature);
- MODULE_INIT(run); //#B
- SAMGR_Bootstrap(); //#C
- }
灰掉部分目前我還未涉足,先跳過,但要是理解了下面的內(nèi)容,灰掉部分也就基本上理解了。
5.1 #A分析SYS_INIT(service)
打開//base/startup/services/bootstrap_lite/source/core_main.h 文件,工程編譯是使用gcc編譯器的,所以__GNUC__是有定義的。
把service代進去展開一下:
- #define SYS_INIT(service) \
- do { \
- SYS_CALL(service, 0); \
- } while (0)
- #define SYS_CALL(service, 0) \
- do { \
- InitCall *initcall = (InitCall *)(SYS_BEGIN(service, 0)); \
- InitCall *initend = (InitCall *)(SYS_END(service, 0)); \
- for (; initcall < initend; initcall++) { \
- (*initcall)(); \
- } \
- } while (0)
SYS_BEGIN和SYS_END就不展開了,重點在for循環(huán),可能還是不太好理解,我再把它翻譯成大白話:
- for循環(huán)就是從 initcall 地址開始,到 initend地址(不含)結(jié)束,
- 依次調(diào)用*initcall內(nèi)的地址所指向的函數(shù),執(zhí)行函數(shù)內(nèi)的指令。
- initcall 是一個指針,其內(nèi)容 *initcall是符號__zinitcall_sys_service_start的地址,即 &__zinitcall_sys_service_start,
- initend 是一個指針,其內(nèi)容 *initend 是符號__zinitcall_sys_service_end的地址,即 &__zinitcall_sys_service_end
Hi3516/Hi3518平臺工程,打開 build\lite\platform\......\link.ld
Hi3861工程則是 vendor\hisi\hi3861\hi3861\build\link\link.ld.S
可以看到上面的 start/end 符號,但估計你打開文件,看到里面的東西,心里還是會有很大的問號。
那就直接去看編譯后輸出的map文件:out\wifiiot\Hi3861_wifiiot_app.map,文本編輯器打開該文件,搜索一下:
這里有三個service 要 init,從抓回來的log看,確實如此:
這下應(yīng)該夠清楚了吧?
SYS_INIT(service) 是調(diào)用端的宏,對應(yīng)的,定義端也有一個對應(yīng)的宏SYS_SERVICE_INIT(xxx)。
工程代碼全局搜索一下“SYS_SERVICE_INIT”,把沒什么用的 sample和 .h中的先去掉,就得到四個:
前三個就是上面的三個service。
第四個“SYS_SERVICE_INIT(InitializeRegistry);”在
foundation\distributedschedule\services\samgr_lite\samgr_server\source\samgr_server.c 文件中,
看它的 BUILD.gn 文件“shared_library("server")”,再到上級目錄查看BUILD.gn,
- if (ohos_kernel_type == "liteos_a" || ohos_kernel_type == "linux"){
- features += [
- "samgr_server:server",
- "samgr_client:client",
- ]
- }
這是LiteOS_A或Linux內(nèi)核的平臺才會有的,所以Hi3861平臺的log上看不到這個server的log。
上面的三個service使用的宏SYS_SERVICE_INIT(Init),我們也一步一步展開,
- #define SYS_SERVICE_INIT(func) LAYER_INITCALL_DEF(func, sys_service, "sys.service")
- #define LAYER_INITCALL_DEF(func, layer, clayer) \
- LAYER_INITCALL(func, layer, clayer, 2) //默認優(yōu)先級 2
- #define LAYER_INITCALL(func, layer, clayer, priority) \
- static const InitCall USED_ATTR __zinitcall_##layer##_##func \
- __attribute__((section(".zinitcall." clayer #priority ".init"))) = func
__attribute__((section(“section_name”))) 其作用是將函數(shù)或數(shù)據(jù)放入指定名為"section_name"對應(yīng)的段中。
最后分別得到:
- //.zinitcall.sys.service2.init = Init //bootstrap_service 的Init
- //.zinitcall.sys.service2.init = Init //broadcast_service 的Init
- //.zinitcall.sys.service2.init = Init //hiview_service 的Init
編譯器根據(jù)attribute+section關(guān)鍵字,就把三個Init函數(shù)全都編譯鏈接到了 .zinitcall.sys.service2.init 對應(yīng)的段中,
結(jié)果就是上面Hi3861_wifiiot_app.map文件截圖的樣子。
5.2 #B分析MODULE_INIT(run)
對于MODULE_INIT(Xxx) 和對應(yīng)的SYS_RUN(Xxx)的分析,和上面沒什么差別,只是編譯鏈接的時候,將函數(shù)放到不同的段中去而已。
我在應(yīng)用層放了兩個APP,分別是helloworld和led_example,log中也看到對應(yīng)的log了。
5.3 #C分析SAMGR_Bootstrap()
SAMGER:system ability manager。系統(tǒng)服務(wù)框架子系統(tǒng),這是一個非常核心的子系統(tǒng)了,我會另開一章來分析。不過這里還是先簡單了解一下它在這一步,做了些什么事情。
先看官方文檔:
由于平臺資源有限,且硬件平臺多樣,因此需要屏蔽不同硬件架構(gòu)和平臺資源的不同、以及運行形態(tài)的不同,提供統(tǒng)一化的系統(tǒng)服務(wù)開發(fā)框架[system ability (SA) framework]。根據(jù)RISC-V、Cortex-M、Cortex-A不同硬件平臺,分為兩種硬件平臺,以下簡稱M核、A核。
- M核:處理器架構(gòu)為Cortex-M或同等處理能力的硬件平臺,系統(tǒng)內(nèi)存一般低于512KB,無文件系統(tǒng)或者僅提供一個可有限使用的輕量級文件系統(tǒng),遵循CMSIS接口規(guī)范。
- A核:處理器架構(gòu)為Cortex-A或同等處理能力的硬件平臺,內(nèi)存資源大于512KB,文件系統(tǒng)完善,可存儲大量數(shù)據(jù),遵循POSIX接口規(guī)范。
系統(tǒng)服務(wù)框架基于面向服務(wù)的架構(gòu),提供了服務(wù)開發(fā)、服務(wù)的子功能開發(fā)、對外接口的開發(fā)、以及多服務(wù)共進程、進程間服務(wù)調(diào)用等開發(fā)能力。其中:
- M核:包含服務(wù)開發(fā)、服務(wù)的子功能開發(fā)、對外接口的開發(fā)以及多服務(wù)共進程的開發(fā)框架。
- A核:在M核能力基礎(chǔ)之上,包含了進程間服務(wù)調(diào)用、進程間服務(wù)調(diào)用權(quán)限控制、進程間服務(wù)接口的開發(fā)等能力。
約束
- 系統(tǒng)服務(wù)開發(fā)框架統(tǒng)一使用C開發(fā)。
- 同進程內(nèi)服務(wù)間調(diào)用統(tǒng)一使用IUnknown接口對外象,消息接口統(tǒng)一由IUnknown接口傳遞給本服務(wù)。
- 服務(wù)名和功能名必需使用常量字符串且長度小于16個字節(jié)。
- M核:系統(tǒng)依賴上bootstrap服務(wù),在系統(tǒng)啟動函數(shù)中調(diào)用OHOS_SystemInit()函數(shù)。
- A核:系統(tǒng)依賴samgr庫,在main函數(shù)中調(diào)用SAMGR_Bootstrap()函數(shù)。
再看一個完整一點的log:
看log,在 SYS_INIT(service) 這一步,bootstrap_service 首先init,
打開 base\startup\services\bootstrap_lite\source\bootstrap_service.c 查看它的 Init 函數(shù),
- static void Init(void)
- {
- static Bootstrap bootstrap;
- bootstrap.GetName = GetName;
- bootstrap.Initialize = Initialize;
- bootstrap.MessageHandle = MessageHandle;
- bootstrap.GetTaskConfig = GetTaskConfig;
- bootstrap.flag = FALSE;
- printf("[bootstrap_service] SYS_SERVICE_INIT(Init).\n");
- SAMGR_GetInstance()->RegisterService((Service *)&bootstrap);
- }
它會先去get一個SAMGR的instance實例,向這個實例注冊bootstrap服務(wù)。
進入SAMGR_GetInstance(),在 foundation\distributedschedule\services\samgr_lite\samgr\source\samgr_lite.c文件中:
bootstrap_service 是第一個調(diào)用SAMGR_GetInstance() 的服務(wù),這時候全局變量g_samgrImpl還沒有初始化,所以就要先init,然后就可以返回instance給Bootstrap 注冊服務(wù)用了,后面的broadcast_service、hiview_service在 init時,直接就可以拿到instance去注冊了。
全局變量g_samgrImpl記錄了向它注冊的所有服務(wù)的信息,包括了一組四個函數(shù):
GetName/Initialize/MessageHandle/GetTaskConfig,這就是上面提到的“boostrap標識的入口函數(shù)”。
bootstrap_service、broadcast_service、hiview_service在SYS_INIT(service)這一步只能做很簡單的注冊服務(wù)的事情,否則會導(dǎo)致后面的INIT受阻。
在SAMGR_Bootstrap(); 這一步時,SAMGR才會真正根據(jù)注冊在g_samgrImpl的信息,逐一為已注冊的服務(wù)創(chuàng)建和分配資源,InitializeAllServices,AddTaskPool,SAMGR_StartTaskPool,SAMGR_SendSharedDirectRequest等待系統(tǒng)調(diào)度,然后在HandleInitRequest中才真正調(diào)用各自serveice注冊的Initialize接口去完成服務(wù)的啟動,為系統(tǒng)提供服務(wù)。
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)