鴻蒙系統的啟動流程
鴻蒙系統的啟動流程
Liangkz 2021.04.11 Ver1.0
- 目錄
- 1. 第一階段:U-Boot啟動
- 2. 第二階段:匯編代碼引導LiteOS-a內核
- 3. 第三階段:內核LiteOS-a的C語言啟動階段
- 4. 第四階段:鴻蒙系統應用層的啟動
- 5. 鴻蒙應用(APP)的啟動
- 6. #task命令查看進程/線程信息(簡表)
聲明:
嚴格來說本文檔并非真正原創的,這是上了朱有鵬老師的免費課《想讀懂鴻蒙2.0源碼,也許你需要先懂這些》之后,做的一些總結。
課程時間一個半小時,內容也很多,學習過程中我發現朱老師的ppt上部分代碼/文件,在我本地的鴻蒙系統代碼上找不到,或者路徑不相同,所以我就做了一些整理。
這里僅摘取課程中的鴻蒙系統在HI3516DV300平臺上的啟動流程部分(從30:00開始講解啟動過程)進行匯總和整理,如有錯誤,請朱老師和各位同學指正。后繼在學習過程中會繼續對本文當作修正升級。
我的本地代碼是基于最新發布的OpenHarmony 1.1.0 LTS(2021-04-01)版本抓取的,代碼根目錄OHOS1_1_0LTS:
- $repo init -u https://gitee.com/openharmony/manifest.git -b refs/tags/OpenHarmony_release_v1.1.0 --no-repo-verify
- $repo sync
在根目錄下執行:
- OHOS1_1_0LTS$ hb set
- [OHOS INFO] Input code path: .
- OHOS Which product do you need?
- ->ipcamera_hispark_taurus@hisilicon
- OHOS1_1_0LTS$ hb build
即可開始編譯 hi3516dv300 平臺代碼。
輸出的過程文件和最終bin,在以下路徑內:
- out\hispark_taurus\ipcamera_hispark_taurus
因為本人還沒有開發板,無法燒錄、抓取log分析以及做相關的操作去驗證。
1. 第一階段:U-Boot啟動
- System startup
- Uncompress Ok!
- U-Boot 2016.11 (......) hi3516dv300
- ............
- ............(省略)
- Hit any key to stop autoboot: 0
- MMC read: dev #0, block # 2048, count 16384 ... 16384 blocks read: OK
- ## Starting application at 0x80000000...
到此為止屬于U-Boot的啟動。
Uboot不屬于鴻蒙系統,這里不做進一步分析,代碼在目錄
- device\hisilicon\third_party\uboot\u-boot-2020.01
2. 第二階段:匯編代碼引導LiteOS-a內核
Uboot引導liteos-a內核啟動起來,需要有一個入口,在:
- kernel\liteos_a\tools\build\liteos.ld
打開這個文件,可見:
- ENTRY(reset_vector)
- INCLUDE board.ld
- SECTIONS
- {
- ......
- }
reset_vector 就是整個鴻蒙內核啟動的入口點,這是一個符號,定義在:
- kernel\liteos_a\arch\arm\arm\src\startup\reset_vector_mp.S
同目錄下還有一個reset_vector_up.S文件,因為HI3516是ARM Cortex A7雙核處理器,所以需要看mp(多核)這個文件,up這個是單核的。
打開reset_vector_mp.S文件,找到“reset_vector:”符號,從這里開始跑匯編代碼,引導liteos-a內核的啟動,一直到:
- “
- bl main
- _start_hang:
- b _start_hang
- ”
這里調用一個 main 函數,然后執行 _start_hang 進入死循環,至此匯編代碼階段就結束了。
通過main函數進入內核LiteOS-a啟動的C語言階段。
3. 第三階段:內核LiteOS-a的C語言啟動階段
上面匯編階段調用的main函數,位于:
- kernel\liteos_a\platform\main.c
main函數通過OsSystemInfo();函數里打印下面這些信息
- ******************Welcome******************
- Processor : Cortex-A7*2
- Run Mode : SMP
- GIC Rev : GICv2
- build time : ......
- Kernel : Huawei LiteOS 2.0.0.xxx
- ********************************************
- main core booting up...
- ...
- ...
從這一步的main開始讀liteos-a的C語言源碼,可以直接在鴻蒙代碼的
- kernel\liteos_a\platform\main.c
進行閱讀理解。
- 不過,推薦從下面倉庫拉代碼下來讀,
- 鴻蒙內核源碼注解分析:
- https://gitee.com/weharmony/kernel_liteos_a_note.git
- 這是在鴻蒙官方開源項目 kernel_liteos_a 基礎上,給源代碼加上了給常詳細的中文注解,有利于加快理解。
main函數截圖如下:
Line173行調用的OsMain()函數,位于:
- kenerl\liteos_a\kernel\common\los_config.c
主要做了:
其中的:
- OsTickInit(......); // tick初始化,包含注冊中斷事件
硬件時鐘初始化,啟動節拍,注冊硬中斷
- OsKernelInitProcess(); // 完成內核進程的初始化
調用OsProcessCreateInit(processCB, OS_KERNEL_MODE, "KProcess", 0);
首先創建2號進程 KProcess,最高優先級0,這是一個內核態進程。
鴻蒙進程一共有32個優先級(0-31),其中0-9級為內核進程,用戶進程可配置的優先級有22個(10-31)。
然后創建2號進程的2個子線程 ResourceTask 和 KIdle,詳見代碼。
可以在shell內執行task命令查看進程和線程信息,表格見文末。
注意,此時1號進程還沒有創建,它是用戶態根進程,要到稍微后面才創建。
- OsSwtmrInit(); //軟時鐘模塊初始化
創建Swt_Task(software timmer)線程,父進程是2號進程KProcess,
- OsSystemInit(); //系統初始化
系統軟硬件的初始化,由2號進程 KProcess創建“system_wq”“SystemInit”“memshow_Task”等線程。
“SystemInit”線程:
其入口函數則由內核外部提供,在:
- device\hisilicon\hispark_taurus\sdk_liteos\mpp\module_init\src\system_init.c
里的SystemInit()函數:
其中的:
- ProcFsInit()
創建和掛載/proc文件系統,代碼見:
- kernel\liteos_a\fs\proc\os_adept\proc_init.c
- SDK_init() //calling SDK_init form HISI_SDK
初始化3516DV300特有的SDK,用內部的DSP硬件來做視頻編解碼,只提供相關庫文件,不開源。
代碼在:
- device\hisilicon\hispark_taurus\sdk_liteos\mpp\module_init\src\sdk_init.c
- OsMountRootfs()
掛載根文件系統:
- out\hispark_taurus\ipcamera_hispark_taurus\rootfs.tar
可以通過tar -tf rootfs.tar 命令查看里面都有些什么內容。
開始查找根文件系統里的/bin/init并創建init進程。
- OsUserInitProcess()
調用OsProcessCreateInit(processCB, OS_USER_MODE, "Init", OS_PROCESS_USERINIT_PRIORITY); //28
這時候才創建1號進程init,這是用戶態根進程,優先級別為28,這個1號進程接下來會創建和啟動其他的用戶態進程(shell/apphilogcat/.../ai_server等3~9號進程)。
【接下來這一小部分還沒理解透】
調用OsLoadUserInit() load init 的相關配置
再使用 __user_init_entry 參數,調用OsUserInitProcessStart()。
__user_init_entry就是第一個用戶態根進程的地址,它通過宏LITE_USER_SEC_ENTRY進行定義,
代碼在:
- kernel\liteos_a\kernel\user\src\los_user_init.c
/bin/init就是kernel調用init_lite的入口【見第四階段對這個的解釋】,由此進入應用層的啟動。
跑完OsMain()函數,LiteOS-a內核的啟動工作就基本上完成了,接下來就開始了鴻蒙系統應用層的啟動。
4. 第四階段:鴻蒙系統應用層的啟動
這個應用層實際上就是鴻蒙的framework,啟動init入口在:
- base\startup\init_lite\services\src\main.c
- ReadFileToBuf()
這一步讀取的 /etc/init.cfg文件,在上面OsMountRootfs()掛載根文件系統的時候就掛載上了,它是
- vendor\hisilicon\hispark_taurus\init_configs\init_liteos_a_3516dv300.cfg
的副本,這個文件就包含了“pre-init”“init”“post-init”的相關操作,分別是設置掛載一些設備、設置好路徑,啟動服務等工作。
而后面的"services"則包含一組服務的定義,它們是系統里的關鍵進程。
- DoJob("init")
由“1號進程init”,在應用層通過start指令創建和啟動:shell/apphilogcat/.../ai_server等3~9號進程,它們都是用戶態進程,父進程都是“1號進程init”。
init將根據上面cfg配置的job和services來做對應的操作和啟動對應的服務程序,并設置它們的uid、gid、進程優先級和權限等。
可以在shell內執行task命令查看進程和線程信息,表格見文末。
- 【見官方文檔:base\startup\init_lite\README_zh.md】
- 這個init組件(即base\startup\init_lite)負責處理從內核加載第一個用戶態進程(2號進程init)開始,到第一個應用程序啟動之間的系統服務進程啟動過程。
- init將系統啟動分為三個階段:
- “pre-init”階段:啟動系統服務之前需要先執行的操作,例如掛載文件系統、創建文件夾、修改權限等
- “init”階段:系統服務啟動階段
- “post-init”階段:系統服務啟動完后還需要執行的操作
- 上述每個階段在配置文件init.cfg中都用一個job表示,每個job都對應一個命令集合,init通過依次執行每個job中的命令來完成系統初始化。job執行順序:先執行“pre-init”,再執行“init”,最后執行“post-init”,所有job都集中放在init.cfg的jobs數組中。
- 除上述jobs數組之外,init.cfg中還有一個services數組,用于存放所有需要由init進程啟動的系統關鍵服務的服務名、可執行文件路徑、權限和其他屬性信息。
- 配置文件init.cfg位于代碼倉庫/vendor/hisilicon/hispark_aries/init_configs/目錄,部署在/etc/下,采用json格式,文件大小目前限制在100KB以內。
- init組件會編譯成out\hispark_taurus\ipcamera_hispark_taurus目錄下的bin/init,同時打包在根文件系統rootfs.tar內,上面掛載根文件系統時,會掛載成/bin/init,由第三階段的最后一步OsUserInit()調用和執行。
- 【見官方文檔:base\startup\init_lite\README_zh.md】
至此,鴻蒙系統的關鍵系統進程和相關服務都已經啟動起來了,至于shell/apphilogcat/.../ai_server等3~9號進程的具體啟動過程和調用的相關代碼入口,還需待后面進一步學習分析。
5. 鴻蒙應用(APP)的啟動
在hi3516dv300平臺(帶屏幕)的桌面(也就是launcher進程)上點擊camera應用圖標,這時候會啟動camera應用程序,實際上會通過“7號進程appspawn”創建子進程“com.huawei.camera”,這是一個應用程序進程,其父進程并不是launcher進程,而是appspawn進程。
實際上所有的應用程序的父進程都是appspawn進程。
鴻蒙應用開發的第一個示例程序“helloworld”的啟動也應該類似。
- #include <stdio.h>
- #include "ohos_init.h"
- #include "ohos_types.h"
- void HelloWorld(void)
- {
- printf("[Init] Hello World!\n");
- }
- SYS_RUN(HelloWorld);
關于SYS_RUN()如何運作,以便讓HelloWorld運行起來,其他老師有非常詳細的解釋,這里不再復述。
6. #task命令查看進程/線程信息(簡表)