liteos分散加載(十四)


1. 概述

1.1 基本概念

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

嵌入式系統通過uboot加載flash上的鏡像文件到內存並執行,而鏡像文件本身可能較大,由於flash讀取速度的限制,將鏡像全部加載完再執行可能無法滿足時間敏感的業務對啟動速度的要求。

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

Huawei LiteOS的分散加載

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

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

1.2 運作機制

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

在這些業務得到執行之后,第一階段的代碼中調用分散加載接口加載剩余部分鏡像,接着運行鏡像剩余部分的業務。

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

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

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所在函數名,找到該函數定義所在的庫,將該庫添加到庫列表中。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM