liteos分散加載(十四)

1. 概述

1.1 基本概念

分散加載是一種實現特定代碼快速啓動的技術,經過優先加載特定代碼到內存,達到縮短從系統開機到特定代碼執行的時間。可被應用來實現關鍵業務的快速啓動。前端

嵌入式系統經過uboot加載flash上的鏡像文件到內存並執行,而鏡像文件自己可能較大,因爲flash讀取速度的限制,將鏡像所有加載完再執行可能沒法知足時間敏感的業務對啓動速度的要求。linux

分散加載的思想是先加載部分鏡像並執行,這部分鏡像包含了時間敏感的關鍵業務,從而達到快速啓動關鍵業務的效果。app

Huawei LiteOS的分散加載less

Huawei LiteOS的分散加載分爲兩個階段,第一階段經過uboot將關鍵業務部分鏡像加載到內存並執行,待這部分業務獲得執行後,第二階段在代碼中加載剩餘部分鏡像到內存繼續執分散加載的內部原理圖如圖2所示,圖中的運做順序可參照圖1的流程說明。經過合理佈局鏡像,第一階段加載部分鏡像的速度會比加載完整鏡像快,從而縮短系統啓動到關鍵業務運行的時間。函數

在IPC Huawei LiteOS版本上,經過應用分散加載技術,實現了1s內從開機啓動到錄製,超越Linux版本的3s-4.5s。工具

1.2 運做機制

分散加載的主體思想是將部分時間敏感的業務提早加載執行,具體手段是將與這些業務相關的數據、代碼段佈局到鏡像文件的前端,第一階段只加載前端這段鏡像,達到最短期內便可運行時間敏感業務的開發指導目的。佈局

在這些業務獲得執行以後,第一階段的代碼中調用分散加載接口加載剩餘部分鏡像,接着運行鏡像剩餘部分的業務。命令行

分散加載的內部原理圖如圖2所示,圖中的運做順序可參照圖1的流程說明。debug

分散加載在關鍵業務第一時間被加載執行以後,再加載非關鍵業務。code

2. 開發指導

2.1 使用場景

分散加載技術應用的典型場景是快速啓動對時間敏感的業務。

嵌入式系統中可能存在某些業務對啓動時間要求比較高,譬如Huawei LiteOS IPC項目上對從開機到錄製預覽的時間要求較高,能夠利用分散加載技術實現錄製預覽業務的快速啓動。

2.2 功能

Huawei LiteOS系統中的分散加載模塊爲用戶提供以下接口。

功能分類 接口名 描述
分散加載接口 LOS_ScatterLoad 在分散加載階段的最後調用此接口,從鏡像加載剩餘非緊急業務

2.3 開發流程

分散加載流程圖以下所示。

步驟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

編譯後,命令行界面會返回緊急鏡像大小信息,以下圖所示。

  • 編譯完成後,檢查鏡像段的排布,若是鏡像中生成了分散加載相關的段則代表分散加載的鏡像生成成功。進入系統鏡像生成目錄(hi3516a平臺的鏡像生成目錄爲out/hi3516a,其餘類推),能夠看到生成的系統鏡像vs_server文件,執行命令readelf -S vs_server打開該文件,結果以下圖所示。顯示了與分散加載相關的段信息(包括段的名稱、起始地址及偏移大小)。其中.fast_rodata爲分散加載鏡像的只讀數據段, .fast_text爲代碼段, .fast_data爲數據段

查看分散加載連接腳本.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地址處加載鏡像。

3. 注意事項

  • 分散加載第一階段拷貝過少或者拷貝偏移地址沒有根據存儲介質的差別進行對齊都會致使系統異常,所以使用時要按照編譯最後給出的大小進行uboot加載鏡像。
  • 用戶需保證提取的庫文件列表是支持關鍵業務運行的超集,不然會致使分散加載第一階段中的代碼訪問到第二階段中的代碼或數據,從而致使系統異常。
  • 分散加載使用中可能存在這樣一種場景:一個變量在第一階段中運行後值被修改,可是在第二階段加載運行以後,該變量值又成爲一個未初始化的值。這種場景的緣由是該變量在第一階段中使用到,可是並無被歸攏到第一階段中,因此在第一階段修改以後,第二階段加載進內存後該變量值又被覆蓋成未初始化的值。解決的方法是將該變量歸攏到第一階段中,確保第一階段使用到的數據都在快速啓動段中

4. 常見問題彙總

本節介紹使用分散加載技術遇到的主要問題和解決方法。

  • 缺乏.O文件
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文件添加到連接腳本中。

  • 分散加載進指令異常。 經過查看系統異常時pc的位置是否超出分散加載第一階段的範圍,若是是則應該是第一階段庫文件列表涵蓋不全,致使有符號未被歸攏到第一階段的代碼、數據段中,須要結合系統鏡像反彙編文件定位到異常pc所在函數名,找到該函數定義所在的庫,將該庫添加到庫列表中。
相關文章
相關標籤/搜索