1 概述
編寫目的: 介紹TinaLinux下啟動速度優(yōu)化使用方法。
2 啟動速度優(yōu)化簡介
啟動速度是嵌入式產(chǎn)品一個重要的性能指標,更快的啟動速度會讓客戶有更好的使用體驗,在某
些方面還會節(jié)省能耗,因為可以直接關(guān)機而不需要休眠。
啟動速度優(yōu)化可提升產(chǎn)品的競爭力。對于某些系統(tǒng)來說,啟動速度是硬性要求。
2.1 啟動流程
TinaLinux系統(tǒng)當前的啟動流程如下:
brom --> boot0 --> (monitor/secure os) --> uboot --> rootfs --> app
后續(xù)將從boot0開始分階段介紹啟動優(yōu)化的方法。
對于某些方案,會存在monitor或secure os,這兩者耗時很短,本文略過。
下文涉及到一些配置文件,提前在此說明。
env配置文件路徑:
tina/device/config/chips//configs//env.cfg #優(yōu)先級高
tina/device/config/chips//configs//linux/env-.cfg #優(yōu)先級中
tina/device/config/chips//configs/default/env.cfg #優(yōu)先級低
sys_config.fex路徑:
tina/device/config/chips//configs//sys_config.fex
uboot-board.dts路徑:
tina/device/config/chips//configs//uboot-board.dts
! 警告:如 果 存 在 uboot-board.dts , uboot 會 使 用 uboot-board.dts 中 配 置; 如 果 不 存 在uboot-board.dts , uboot 會使用 sys_config.fex 中的配置。(AW1886/V853 使用了 uboot-board.dts )
2.2 測量方法
2.2.1 printk time
打開kernel配置,使能如下選項:
kernel hacking --->
[*] Show timing information on printks
linux4.9
kernel hacking --->
printk and dmesg options --->
[*] Show timing information on printks
將會在內(nèi)核的log前加入時間戳。
注:此方法主要用來測量內(nèi)核啟動過程中各個階段的耗時。
2.2.2 initcall_debug
修改env文件,在kernel的cmdline中加入參數(shù),
# 增加initcall_debug變量
initcall_debug=1
?
#將initcall_debug=${initcall_debug}加入setargs_xxx中,如setargs_nand,setargs_mmc,setargs_nor,setatgs_nand_ubi等,
setargs_nand=setenv bootargs console=${console} earlyprintk=${earlyprintk} root=${nand_root} initcall_debug=${initcall_debug} init=${init}
開啟之后,啟動中會打印每個initcall函數(shù)調(diào)用及其耗時。
注:此方法主要用來測量內(nèi)核initcall的耗時。
一般需同時配置上內(nèi)核符號表,即kallsyms選項,以打印函數(shù)名。
2.2.3 bootgraph.
在內(nèi)核源碼中自帶了一個工具(scripts/bootgraph.pl)可用于分析啟動時間,需要把log_buff加 大,要不然會丟失最早的啟動信息:
make kernel_menuconfig
General setup --->
(17) Kernel log buffer size (16 => 64KB, 17 => 128KB)
Kernel hacking --->
printk and dmesg options --->
[*] Show timing information on printks
kernel編譯時需要包含CONFIG_PRINTK_TIME選項。
在kernel cmdline加上"initcall_debug=1"。
在系統(tǒng)啟動完畢后執(zhí)行"dmesg | perl $(Kernel_DIR)/scripts/bootgraph.pl > output.svg"。
使用SVG瀏覽器(比如Inkscape,Gimp,F(xiàn)irefox等)來查看輸出文件output.svg。
注:此方法主要用來測量內(nèi)核啟動過程中各個階段的耗時。
2.2.4 bootchart
bootchart是一個用于linux啟動過程性能分析的開源軟件工具,在系統(tǒng)啟動過程自動收集CPU 占用率、進程等信息,并以圖形方式顯示分析結(jié)果,可用作指導(dǎo)優(yōu)化系統(tǒng)啟動過程。
修改kernel cmdline。修改env配置文件(路徑見上文說明),將其中的init修改為"init=/ sbin/bootchartd"。
收集信息。bootchartd會從/proc/stat,/proc/diskstat,/proc/[pid]/stat中采集信息,經(jīng) 過處理后保存為bootchart.tgz文件。
轉(zhuǎn)換圖片。在PC上通過pybootchartgui.py工具將bootchart.tgz轉(zhuǎn)換為bootchart.png, 方便分析。
注:此方法主要用來測量掛載文件系統(tǒng)到主應(yīng)用程序啟動過程中的耗時。
2.2.5 gpio +示波器.
在適當?shù)牡胤郊尤氩僮鱣pio的代碼,通過示波器抓取波形得到各階段耗時。
注:此方法可用來測量整個啟動中各階段的耗時。
2.2.6 grabserial.
Grabserial是Tim Bird用python寫的一個抓取串口的工具,這個工具能夠為收到的每一行信 息添加上時間戳??蓮娜缦侣窂?a href="http://wenjunhu.com/soft/special/" target="_blank">下載使用:https://github.com/tbird20d/grabserial
介紹文檔:http://elinux.org/Grabserial
常見的用法:
sudo grabserial -v -S -d /dev/ttyUSB0 -e 30 -t
如果要在某個字符串重置時間戳,可以使用-m參數(shù):
sudo grabserial -v -S -d /dev/ttyUSB0 -e 30 -t -m "Starting kernel"
-v顯示參數(shù)等信息。
-s跳過對串口的檢查。
-d指定串口,如上述為指定/dev/ttyUSB0為操作的串口。
-e參數(shù)指定時間,如上述命令表示抓取30s的串口記錄。
-t表示加上時間戳。
-m匹配到指定字符串就重置時間戳的時間,也就是從 0 開始。
更多配置可以使用-h參數(shù)查看幫助。
注:此方法可用來測量整個啟動中各階段的耗時。
2.3 優(yōu)化方法
注:本節(jié)提供一些優(yōu)化方法以供參考,并非所有都在Tina上集成,主要原因有:
優(yōu)化沒有止境。需要根據(jù)目標來選擇優(yōu)化方法,綜合考慮優(yōu)化效果與優(yōu)化難度。
優(yōu)化需要具有針對性。由于各方案CPU個數(shù)及頻率、flash類型及大小、kernel/rootfs壓縮 類型與尺寸、所需功能、主應(yīng)用等的不同,需要針對性的進行優(yōu)化。
2.3.1 boot0啟動優(yōu)化
boot0運行在SRAM,主要功能是對DRAM進行初始化,并將uboot、monitor、secure-os 等加載至DRAM。
2.3.1.1 非安全啟動.
boot0可優(yōu)化的地方不多,可以做的是:
關(guān)閉串口輸出。
減少檢測按鍵和檢測串口的等待時間。
加載uboot的時候,不要先加載后搬運,直接加載到uboot的運行地址。
對于spinor的方案,還可以直接從boot0啟動,只需要在boot0中加載好kernel和dtb, 不需要經(jīng)過uboot ,然后直接跳轉(zhuǎn)到kernel運行,可節(jié)省一定的時間。如果采用boot0啟 動OS,則boot0讀取數(shù)據(jù)量較大,其flash驅(qū)動也需要進行優(yōu)化,如提高時鐘,開啟雙線/四 線/DMA/Cache等。
2.3.1.2 安全啟動
對于安全方案來說,boot0還會對uboot、monitor、secure-os等進行簽名校驗,因為在啟動 時需要引導(dǎo)SecoreOS,需要做一次環(huán)境切換,CPU由安全狀態(tài)切換到非安全狀態(tài)運行,所以對 于安全方案來說,不支持直接從boot0啟動,然后加載dtb和kernel到內(nèi)存,然后直接啟動內(nèi) 核,主要的優(yōu)化手段較少,可以做的是:
關(guān)閉串口打印。
減少檢測按鍵和檢測串口的等待時間。
加載uboot的時候,不要先加載后搬運,直接加載到uboot的運行地址。
2.3.2 uboot啟動優(yōu)化
uboot主要功能是引導(dǎo)內(nèi)核、量產(chǎn)升級、電源管理、開機音樂/logo、fastboot刷機等。
2.3.2.1 完全去掉uboot
uboot的包含很多重要功能,通常會保留。某些情況可以去掉,直接從boot0加載內(nèi)核并啟動, 可節(jié)省一些時間。
2.3.2.2 避免burnkey的影響
對于啟用了burnkey支持,且還沒使用DragonSN工具將key燒錄進去的板子,每次啟動到 uboot都會嘗試跟PC端工具交互產(chǎn)生如下log,帶來延時。
[1.334]usb burn from boot ... [1.400]usb prepare ok usb sof ok [1.662]usb probe ok [1.664]usb setup ok ... [4.698]do_burn_from_boot usb : have no handshake
如果產(chǎn)品不需要 burnkey,可將 uboot-board.dts 或 sys_config.fex 中的 [target] 下 burn_key設(shè)置為 0 。
或者使用DragonSN工具,燒錄一次key,并設(shè)置燒錄標志,以使后續(xù)啟動可跳過檢測。
2.3.2.3 提高CPU以及flash讀取頻率
可設(shè)置uboot-board.dts或sys_config.fex中的[target]下boot_clock來修改uboot運行 時CPU頻率( 注:不能超過SPEC最大頻率 )。
對于spinor/spinand,使用較高的時鐘頻率(一般是100M),使用四線模式或者雙線模式(看 硬件是否支持),提高加載速度。
2.3.2.4 關(guān)閉串口輸出.
可將uboot-board.dts或sys_config.fex中的[platform]下debug_mode設(shè)置為 0 來關(guān)閉 uboot的串口輸出。
可將sys_config.fex中的[platform]下debug_mode設(shè)置為 0 來關(guān)閉boot0串口輸出。
配置此項后,如果還有少量輸出,有兩個可能的原因:
第一是這些輸出是在獲取debug_mode流程之前產(chǎn)生。
第二是因為源碼中直接使用了puts而沒有使用printf。
對于這兩者情況,需要修改源碼來完全關(guān)閉串口輸出。
2.3.2.5 修改kernel加載位置
如果uboot將內(nèi)核加載到DRAM的地址與內(nèi)核中l(wèi)oad address不匹配,就需要將內(nèi)核移動到 正確位置,這樣會浪費一定的時間。因此,可以直接修改uboot加載內(nèi)核為正確的地址。
具體是修改env文件(路徑見上文)的boot_normal與boot_recovery變量。
需要根據(jù)不同的內(nèi)核鏡像格式來設(shè)置不同的值 。
假設(shè)kernel的load address為0x40008000。
如果使用的是uImage,也就是在kernel的鏡像前加了 64 字節(jié),所以uboot應(yīng)該將kernel 加載到0x40008000 - 0x40 = 0x40007fc0。
#uImage/raw boot_normal=sunxi_flash read 40007fc0 ${boot_partition};bootm 40007fc boot_recovery=sunxi_flash read 40007fc0 recovery;bootm 40007fc
如果使用的是boot.img,即android的kernel格式,其頭部大小為0x800,所以uboot應(yīng)該將kernel加載到0x40008000 - 0x800 = 40007800。
#boot.img/raw boot_normal=sunxi_flash read 40007800 ${boot_partition};bootm 40007800 boot_recovery=sunxi_flash read 40007800 recovery;bootm 40007800
如果uboot加載kernel地址與load address不匹配,uboot過程中串口輸出可能會有:
Loading Kernel Image ... OK
如果是匹配的,uboot過程中串口輸出可能會有:
XIP Kernel Image ... OK
2.3.2.6 修改kernel加載大小
最新代碼會根據(jù)uImage/boot.img的頭部信息,只讀取必要的大小,可忽略此優(yōu)化項。
對于舊代碼,uboot在加載內(nèi)核的時候,有些情況會直接將整個分區(qū)讀取出來,uboot-2018會自 讀取kernel鏡像的大小。
就是說假如內(nèi)核只有2M,而分區(qū)分了4M的話,uboot就會讀取4M。這種情況下,可以將分 區(qū)大小設(shè)置得剛好容納下內(nèi)核,這樣可避免uboot在加載內(nèi)核的時候浪費時間。
nor方案修改sys_partiton_nor.fex的boot分區(qū)大小
nand/emmc可修改sys_partition.fex中boot分區(qū)的大小。
uboot具體讀出多少,通常會有l(wèi)og信息,可同真正內(nèi)核鏡像的size進行比較。
2.3.2.7 關(guān)閉kernel校驗
uboot加載了內(nèi)核以后,默認會對內(nèi)核進行校驗,可以在串口輸出中看到:
Verifying Checksum ... OK
如果不想校驗可以去掉,目前的情況是可以減少幾十毫秒(不同平臺,不同內(nèi)核大小,時間不同) 的啟動時間。
具體修改env配置文件(路徑見上文),新增一行"verify=no"。
2.3.2.8 uboot重定位
目前的啟動過程中,uboot在執(zhí)行過程中會進行一次重定位,可以在串口中打印出這個值,然后 修改uboot的加載地址使得boot0將uboot加載進DRAM的時候就直接加載到這個地址。
對于uboot-2014版本的位置為tina/lichee/brandy/u-boot/include/configs/suniwp.h 中的
c #define CONFIG_SYS_TEXT_BASE=0x
對于uboot-2018在對應(yīng)的configs/suniwp*_defconfig文件中
c CONFIG_SYS_TEXT_BASE=0x
但這個方法有個弊端,如果后續(xù)修改了uboot的代碼,則可能需要重新設(shè)置。
目前這個操作耗時很少(某平臺測得十幾毫秒),不必要的話不建議做這個修改。
2.3.2.9 裁剪uboot.
即使流程沒有簡化,uboot體積的減小也可減少加載uboot的時間。
依據(jù)具體情況,可對uboot不需要的功能的模塊進行裁剪,避免了啟動中執(zhí)行不必要的流程,可 減少uboot加載時間。
2.3.2.10開啟logo及音樂.
可嘗試在uboot中開啟開機logo/音樂,盡快播出第一幀/聲,提升用戶體驗。
此操作會延緩到達OS/APP的時間,但如果產(chǎn)品定義/用戶體驗是以第一幀/聲為準的話,則有較 大價值。
2.3.3 kernel啟動優(yōu)化.
通常來說,內(nèi)核啟動耗時較多,需要更深入的優(yōu)化。
2.3.3.1 kernel壓縮方式.
比較不同壓縮方式的啟動時間和flash占用情況,選擇一種符合實際情況的。
此處給出某次測試結(jié)果供參考。實際優(yōu)化的時候,需要重新測試,根據(jù)實際情況選擇。
壓縮方式 | 內(nèi)核大小/M | 加載時間/s | 解壓時間/s | 總時間/s |
---|---|---|---|---|
LZO | 2.4 | 0.38 | 0.23 | 0.61 |
GZIP | 1.9 | 0.35 | 0.44 | 0.79 |
XZ | 1.5 | 0.25 | 2.17 | 2.42 |
2.3.3.2 加載位置
內(nèi)核鏡像可以由kernel自解壓,也有uboot進行解壓的情況。
對于kernel自解壓的情況,如果壓縮過的kernel與解壓后的kernel地址沖突,則會先把自己 復(fù)制到安全的地方,然后再解壓,防止自我覆蓋。這就需要耗費復(fù)制的時間。
比如對于運行地址為0x40008000的內(nèi)核來說,bootloader可以將其加載到0x41008000, 當然其他位置也可以。
2.3.3.3 內(nèi)核裁剪
裁剪內(nèi)核,帶來的加速是兩個方面的。一是體積變小,加載解壓耗時減少;二是內(nèi)核啟動時初始
化內(nèi)容變少。
裁剪要根據(jù)產(chǎn)品的實際情況來,將不需要的功能及模塊都去掉。
具體是執(zhí)行"make kernel_menuconfig",關(guān)閉不需要的選項??蓞⒖肌禩inaLinux_系統(tǒng)裁剪開發(fā)指 南.pdf》。
2.3.3.4 預(yù)設(shè)置lpj數(shù)值
LPJ也就是loops_per_jiffy,每次啟動都會計算一次,但如果沒有做修改的話,這個值每次啟動 算出來都是一樣的,可以直接提供數(shù)值跳過計算。
如下log所示,有skipped,lpj由timer計算得來,不需要再校準calibrate了。
[ 0.019918] Calibrating delay loop (skipped), value calculated using timer frequency.. 48.00 BogoMIPS (lpj=240000)
如果沒有skipped,則可以在cmdline中添加lpj=XXX進行預(yù)設(shè)。
2.3.3.5 initcall優(yōu)化
在cmdline中設(shè)置initcall_debug=1,即可打印跟蹤所有內(nèi)核初始化過程中調(diào)用initcall的順 序以及耗時。
具體修改env配置文件(路徑見上文),新增一行"initcall_debug=1",并在"setargs_*"后加入" initcall_debug=${initcall_debug}",如下所示。
setargs_nand=setenv bootargs console=${console} console=tty0 root=${nand_root} init=${init} loglevel=${loglevel} partitions=${partitions} initcall_debug=${initcall_debug}
加入后,內(nèi)核啟動時就會有類似如下的打印,對于耗時較多的initcall,可進行深入優(yōu)化。
[ 0.021772] initcall sunxi_pinctrl_init+0x0/0x44 returned 0 after 9765 usecs [ 0.067694] initcall param_sysfs_init+0x0/0x198 returned 0 after 29296 usecs [ 0.070240] initcall genhd_device_init+0x0/0x88 returned 0 after 9765 usecs [ 0.080405] initcall init_scsi+0x0/0x90 returned 0 after 9765 usecs [ 0.090384] initcall mmc_init+0x0/0x84 returned 0 after 9765 usecs
2.3.3.6 內(nèi)核initcall module并行
內(nèi)核initcall有很多級別,其中啟動中最耗時的就是各module的initcall,針對多核方案,可 以考慮將module initcall并行執(zhí)行來節(jié)省時間。
目前內(nèi)核do_initcalls是一個一個按照順序來執(zhí)行,可以修改成新建內(nèi)核線程來執(zhí)行。
注:當前Tina還未加入該優(yōu)化。
2.3.3.7 減少pty/tty個數(shù)
加入initcall打印之后,部分平臺發(fā)現(xiàn)pty/tty init耗時很多,可減少個數(shù)來縮短init時間。
initcall pty_init+0x0/0x3c4 returned 0 after 239627 usecs initcall chr_dev_init+0x0/0xdc returned 0 after 36581 usecs
2.3.3.8 內(nèi)核module.
需要考慮啟動速度的界定,對于內(nèi)核module的優(yōu)化主要有兩點:
對于必須要加載的模塊,直接編譯進內(nèi)核
對于不急需的功能,可以編譯成模塊。
比如某個應(yīng)用,會開啟主界面聯(lián)網(wǎng),啟動速度以出現(xiàn)主界面為準,那么可以考慮將disp編入內(nèi) 核,wifi編譯成模塊,后續(xù)需要時再動態(tài)加載。
2.3.3.9 Deferred Initcalls
介紹頁面及patch:http://elinux.org/Deferred_Initcalls
打上這個patch之后,可以標記一些initcall為Deferred_Initcall。這些被標記的初始化函 數(shù),在系統(tǒng)啟動的時候不會被調(diào)用
進入文件系統(tǒng)后,在合適的時間,比如啟動主應(yīng)用之后,再通過文件系統(tǒng)接口,啟動這些推遲了 的調(diào)用,徹底完成初始化。
2.3.4 rootfs啟動優(yōu)化
rootfs啟動優(yōu)化主要是優(yōu)化rootfs的掛載到init進程執(zhí)行。
2.3.4.1 initramfs
initramfs是一個內(nèi)存文件系統(tǒng),會占用較多DRAM。
部分產(chǎn)品可能會用到initramfs來過渡到rootfs,其優(yōu)化思路大體與rootfs類似??蓞⒖急竟?jié)后 續(xù)的優(yōu)化方案。
2.3.4.2 rootfs類型以及壓縮.
存儲介質(zhì)、文件系統(tǒng)類型,壓縮方式對rootfs掛載有很大影響。
此處給出某次測試結(jié)果供參考。實際優(yōu)化的時候,需要重新測試,根據(jù)實際情況選擇。
類型 | 壓縮 | 介質(zhì) | 總時間/s |
---|---|---|---|
squashfs | gzip | emmc | 0.12 |
squashfs | xz | emmc | 0.27 |
squashfs | xz | nand | 0.26 |
ext4 | - | emmc | 0.12 |
2.3.4.3 rootfs裁剪
文件系統(tǒng)越小,加載速度越快。裁剪的主要思路是:刪換壓,即刪除沒有用到的,用小的換大
的,選擇合適的壓縮方式。
2.3.4.4 指定文件系統(tǒng)類型
內(nèi)核在掛載rootfs時,會有一個try文件系統(tǒng)類型的過程??梢栽赾mdline直接指定,節(jié)省時 間。
具體是在cmdline中添加"rootfstype=",其中type為文件系統(tǒng)類型,如ext4、squashfs 等。
2.3.4.5 靜態(tài)創(chuàng)建dev節(jié)點.
對于dev下面的節(jié)點,事先根據(jù)實際情況創(chuàng)建好,而不是在系統(tǒng)啟動后動態(tài)生成,理論上也可以 節(jié)省一定的時間。
2.3.4.6 rootfs拆分
可以將rootfs拆分成兩個部分,一個小的文件系統(tǒng)先掛載執(zhí)行,大的文件系統(tǒng)根據(jù)需要動態(tài)掛 載。
2.3.5 主應(yīng)用程序啟動優(yōu)化.
主應(yīng)用程序主要是由客戶開發(fā),因此主導(dǎo)優(yōu)化的還是客戶,這里提一些優(yōu)化措施:
提升運行順序。將應(yīng)用程序放在init很前面執(zhí)行。
動態(tài)/靜態(tài)鏈接。
編譯選項。
暫時不使用的庫采用dlopen方式。
應(yīng)用程序拆分。
3 Tina啟動速度優(yōu)化
Tina中啟動優(yōu)化主要依靠宏CONFIG_BOOT_TIME_OPTIMIZATION來完成,該宏會進行如 下工作:
調(diào)整Linux內(nèi)核鏡像的壓縮方式,調(diào)整rootfs的壓縮方式。具體如何調(diào)整需要依據(jù)具體方案進 行預(yù)先設(shè)定。
使boot0、uboot、kernel的打印不會輸出到控制臺。具體是在scripts/pack_img.sh腳本 中完成。
uboot加載內(nèi)核時不進行校驗。具體是在scripts/pack_img.sh腳本中完成。
設(shè)置內(nèi)核命令行參數(shù)rootfstype,某些情況下會加快根文件系統(tǒng)的加載。具體是在scripts/- pack_img.sh腳本中完成。
部分方案會調(diào)整kernel鏡像的加載地址。具體是在scripts/pack_img.sh腳本中完成。
注:通過該宏預(yù)計可達到70%左右的優(yōu)化效果,如還需優(yōu)化,可參考第二章的內(nèi)容。
3.1 開啟Tina啟動速度優(yōu)化.
在tina根目錄下執(zhí)行make menuconfig使能CONFIG_BOOT_TIME_OPTIMIZATION, 具體如下所示
Tina Configuration └─> Target Images └─>[*] kernel compression mode setting ---- └─>Compression (Gzip) ---> └─> ( ) Gzip ( ) LZMA ( ) XZ (X) LZO └─>[*] Boot Time Optimization
注:如果看不到該選項,使用?鍵搜索,會發(fā)現(xiàn)此項有一些依賴選項,使能依賴選項即可看到
Boot Time Optimization
3.2 實驗結(jié)果
在某norflash方案上開啟CONFIG_BOOT_TIME_OPTIMIZATION后,啟動速度提升效果 如下:
Linux內(nèi)核鏡像壓縮方式從GZIP換成LZO,優(yōu)化> 0.2s。
rootfs從squashfs XZ壓縮換成squashfs GZIP壓縮,優(yōu)化> 0.15s。
屏蔽boot0、uboot、kernel啟動階段控制臺打印,優(yōu)化> 2s。
取消內(nèi)核加載時的校驗,優(yōu)化0.3~0.4s。
注:對于不同的方案,由于CPU運算速度、存儲器類型、內(nèi)核壓縮及尺寸、根文件系統(tǒng)類型及尺
寸、主應(yīng)用等的不同,優(yōu)化結(jié)果會有一定差異,請以實際優(yōu)化結(jié)果為準。
-
內(nèi)核
+關(guān)注
關(guān)注
3文章
1376瀏覽量
40316 -
Linux
+關(guān)注
關(guān)注
87文章
11314瀏覽量
209807 -
優(yōu)化
+關(guān)注
關(guān)注
0文章
220瀏覽量
23926 -
Tina
+關(guān)注
關(guān)注
2文章
45瀏覽量
16994
發(fā)布評論請先 登錄
相關(guān)推薦
評論