分散加載是一種實現特定代碼快速啓動的技術,經過優先加載特定代碼到內存,達到縮短從系統開機到特定代碼執行的時間。可被應用來實現關鍵業務的快速啓動。前端
嵌入式系統經過uboot加載flash上的鏡像文件到內存並執行,而鏡像文件自己可能較大,因爲flash讀取速度的限制,將鏡像所有加載完再執行可能沒法知足時間敏感的業務對啓動速度的要求。linux
分散加載的思想是先加載部分鏡像並執行,這部分鏡像包含了時間敏感的關鍵業務,從而達到快速啓動關鍵業務的效果。app
Huawei LiteOS的分散加載less
Huawei LiteOS的分散加載分爲兩個階段,第一階段經過uboot將關鍵業務部分鏡像加載到內存並執行,待這部分業務獲得執行後,第二階段在代碼中加載剩餘部分鏡像到內存繼續執分散加載的內部原理圖如圖2所示,圖中的運做順序可參照圖1的流程說明。經過合理佈局鏡像,第一階段加載部分鏡像的速度會比加載完整鏡像快,從而縮短系統啓動到關鍵業務運行的時間。函數
在IPC Huawei LiteOS版本上,經過應用分散加載技術,實現了1s內從開機啓動到錄製,超越Linux版本的3s-4.5s。工具
分散加載的主體思想是將部分時間敏感的業務提早加載執行,具體手段是將與這些業務相關的數據、代碼段佈局到鏡像文件的前端,第一階段只加載前端這段鏡像,達到最短期內便可運行時間敏感業務的開發指導目的。佈局
在這些業務獲得執行以後,第一階段的代碼中調用分散加載接口加載剩餘部分鏡像,接着運行鏡像剩餘部分的業務。命令行
分散加載的內部原理圖如圖2所示,圖中的運做順序可參照圖1的流程說明。debug
分散加載在關鍵業務第一時間被加載執行以後,再加載非關鍵業務。code
分散加載技術應用的典型場景是快速啓動對時間敏感的業務。
嵌入式系統中可能存在某些業務對啓動時間要求比較高,譬如Huawei LiteOS IPC項目上對從開機到錄製預覽的時間要求較高,能夠利用分散加載技術實現錄製預覽業務的快速啓動。
Huawei LiteOS系統中的分散加載模塊爲用戶提供以下接口。
功能分類 | 接口名 | 描述 |
---|---|---|
分散加載接口 | LOS_ScatterLoad | 在分散加載階段的最後調用此接口,從鏡像加載剩餘非緊急業務 |
分散加載流程圖以下所示。
步驟1 調用接口LOS_ScatterLoad,編寫分散加載業務代碼
業務代碼入口爲函數app_init,該函數位於os_adapt.c。在緊急業務代碼後調用LOS_ScatterLoad函數進行分散加載,並用#ifndef MAKE_SCATTER_IMAGE、 #endif將該函數後的非緊急業務包圍起來,用以編譯緊急鏡像和所有鏡像時做區分,示例代碼以下:
void app_init() { proc_fs_init(); hi_uartdev_init(); system_console_init("/dev/uartdev-0"); LOS_CppSystemInit((unsigned long)&__init_array_start__, (unsigned long)&__init_array_end__, BEFORE_SCATTER); LOS_ScatterLoad(0x100000, flash_read, NAND_READ_ALIGN_SIZE); #ifndef MAKE_SCATTER_IMAGE /* 如下爲非緊急業務 */ LOS_CppSystemInit((unsigned long)&__init_array_start__, (unsigned long)&__init_array_end__, AFTER_SCATTER); extern unsigned int osShellInit(void); osShellInit(); rdk_fs_init(); SDK_init(); hi_product_driver_init(); char *apszArgv[3]={"vs_server","./higv.bin","-i"}; vs_server(3, apszArgv); #endif /* MAKE_SCATTER_IMAGE */ }
os_adapt.c位於Huawei_LiteOS代碼包的platform/bsp/hi3516a/os_adapt路徑下。
步驟2 配置SCATTER_SRC變量
在根目錄下Makefile中配置SCATTER_SRC,將變量定義爲調用分散加載函數的業務源文件路徑,以下所示,其中LITEOSTOPDIR指代Huawei_LiteOS代碼根目錄。
SCATTER_SRC := $(LITEOSTOPDIR)/platform/bsp/$(LITEOS_PLATFORM)/os_adapt/os_adapt.c
步驟3 執行make scatter,編譯緊急部分鏡像
在根目錄下執行以下命令,則不會編譯#ifndef MAKE_SCATTER_IMAGE如下的業務代碼。編譯系統將自動調用工具鏈抽取分散加載最小鏡像的符號表並根據該符號表提取分散加載最小鏡像的.a庫列表。
Huawei_LiteOS$ make scatter
步驟4 執行make,編譯所有鏡像
Huawei_LiteOS$ make
編譯後,命令行界面會返回緊急鏡像大小信息,以下圖所示。
查看分散加載連接腳本.text段,新增了scatter.o(.text),以下圖所示,實現了將分散加載的快速啓動部分代碼相關符號歸攏到一個同一個段中。
分散加載連接腳本路徑:Huawei_LiteOS/tools/scripts/ld/scatter.ld
步驟5 執行tftp 0x82000000 vs_server.bin;nand erase 0x100000 0x700000;nand write 0x82000000 0x100000 0x700000;,將所有鏡像燒寫到Flash
進入串口工具界面,輸入以下命令,將所有鏡像燒寫到Flash的0x100000地址位。
tftp 0x82000000 vs_server.bin;nand erase 0x100000 0x700000;nand write 0x82000000 0x100000 0x700000;
其中, vs_server.bin爲系統鏡像文件名,先將其燒寫到內存中一段高地址位0x82000000。而後燒寫到Flash,起始地址爲0x100000,燒寫長度爲0x700000,即燒寫的鏡像文件大小不能超過7M,跟據實際鏡像大小調整數值。
步驟6 執行nand read 0x80008000 0x100000 0x4E0000; go 0x80008000;,加載緊急業務
執行以下命令,從Flash的0x100000地址處讀取長度爲0x4E0000的鏡像,加載緊急業務到0x80008000。
nand read 0x80008000 0x100000 0x4E0000; go 0x80008000;
步驟7 系統自動重啓
系統自動重啓,在0x80008000地址處加載鏡像。
本節介紹使用分散加載技術遇到的主要問題和解決方法。
arm-hisiv300-linux-ld: cannot find libscatter.O make: *** [vs_server] Error 1
這個問題出現的緣由是修改了連接腳本後,沒有對應生成.O文件,解決的方法是生成對應的.O文件而且放到目標目錄下
/usr1/xxxxx/gerrit_code/modify-debug/liteos_ipc/out/lib/libar6003.a(ar6000_drv.o): In function `ar6000_avail_ev': /usr1/xxxxx/gerrit_code/modify-debug/liteos_ipc/vendor/ar6k3_wifi/AR6003/host/qca/source/ ar6000_drv.c:1553: undefined reference to `wireless_init_event' /usr1/xxxxx/gerrit_code/modify-debug/liteos_ipc/out/lib/libar6003.a(drv_config.o): In function `ar6000_tkip_micerr_event': /usr1/xxxxx/gerrit_code/modify-debug/liteos_ipc/vendor/ar6k3_wifi/AR6003/host/qca/source/ drv_config.c:1856: undefined reference to `wireless_send_event' make: *** [vs_server] Error 1
這個問題的出現是比較常見的,多是裁剪過程當中在修改連接腳本的時候,將一些必要的.a文件也刪除了,這時須要用grep指令在out/lib目錄下搜索未定義的變量,找出都存在於哪些.a文件中,將未添加的.a文件添加到連接腳本中。