嵌入式Linux啟動(dòng)時(shí)間優(yōu)化的秘密
01
工具鏈/應(yīng)用程序優(yōu)化
導(dǎo)讀:嵌入式Linux在應(yīng)用中往往希望系統(tǒng)能在盡量短的時(shí)間內(nèi)啟動(dòng),以提高用戶體驗(yàn)。而且在有的應(yīng)用場(chǎng)合,對(duì)啟動(dòng)時(shí)間具有嚴(yán)格的時(shí)間要求,尤其在工業(yè)或者醫(yī)療器械應(yīng)用領(lǐng)域。此時(shí)如何加快Linux的啟動(dòng),將成為一個(gè)挑戰(zhàn),對(duì)于大多數(shù)應(yīng)用開(kāi)發(fā)人員而言,由于Linux系統(tǒng)的復(fù)雜性,對(duì)于如何提高啟動(dòng)速度,往往無(wú)從下手。那么閱讀完本文,將獲得清晰完整的解決思路。
1.降低啟動(dòng)時(shí)間的一般思路
在準(zhǔn)備降低系統(tǒng)的啟動(dòng)時(shí)間時(shí),思路上應(yīng)建立以下的切入點(diǎn):
最快的代碼是未執(zhí)行的代碼。
引導(dǎo)操作本質(zhì)上的很大一部分工作實(shí)際上是將代碼和數(shù)據(jù)從存儲(chǔ)設(shè)備加載到RAM。如所需加載內(nèi)容越少則意味著加載操作越快。
如果根文件系統(tǒng)越大,則安裝時(shí)間可能會(huì)越長(zhǎng)。
因此,即使未執(zhí)行的代碼也會(huì)延長(zhǎng)啟動(dòng)時(shí)間。
另外在硬件方案設(shè)計(jì)時(shí)盡量選擇讀寫速度快的存儲(chǔ)介質(zhì)。例如,從SD卡啟動(dòng)實(shí)際上比從NAND FLASH啟動(dòng)快。
2.啟動(dòng)時(shí)間測(cè)量方法
要降低系統(tǒng)的啟動(dòng)時(shí)間,則首先需要選擇一個(gè)可靠的啟動(dòng)時(shí)間的測(cè)量方法:
在Linux代碼中加入對(duì)某一個(gè)GPIO腳的邏輯電平控制,利用示波器測(cè)量GPIO狀態(tài)。后面將介紹如何在代碼中加入對(duì)GPIO的控制。
監(jiān)控串口控制臺(tái)報(bào)文以測(cè)量時(shí)間,可以使用grabserial。
參見(jiàn)https://elinux.org/Grabserial
3. 工具鏈優(yōu)化
3.1 從工具鏈入手
選擇使用合適的工具鏈,應(yīng)是第一個(gè)入手點(diǎn),因?yàn)樗械倪\(yùn)行加載固件都是由工具鏈編譯而成。如果尚未進(jìn)行其他優(yōu)化,則更改工具鏈的好處將更加明顯,并且更容易度量。
您可以在工具鏈中進(jìn)行以下更改,這可能會(huì)影響啟動(dòng)時(shí)間,性能和大?。?/p>
編譯器版本:gcc和binutils的版本,最新版本往往可以具有更好的優(yōu)化功能。
C庫(kù):glibc,uClibc,musl。使用uClibc和musl庫(kù)編譯的根文件系統(tǒng)更小
指令集變量:ARM或Thumb2,是否支持硬浮點(diǎn)。
可能會(huì)影響代碼性能和代碼大小(Thumb2編碼與ARM相同的指令,但以更緊湊的方式,至少會(huì)顯著減小大小)。
C庫(kù)在創(chuàng)建工具鏈時(shí)進(jìn)行了硬編碼,可供選擇的C庫(kù):
glibc:最標(biāo)準(zhǔn)且功能最全。http://www.gnu.org/software/libc/
uClibc:更小且可配置。已經(jīng)存在約20年了。http://uclibc-ng.org/
musl:uClibc替代品,雖比較新但很成熟。http://www.musl-libc.org/
可以對(duì)glibc/uclibc-ng /musl進(jìn)行對(duì)比測(cè)試:
1.靜態(tài)編譯hello.c程序并比較大小
使用gcc 6.3, armel, musl 1.1.16: 7300 字節(jié)
使用gcc 6.3, armel, uclibc-ng 1.0.22 : 67204 字節(jié)
使用gcc 6.2, armel, glibc: 492792 字節(jié)
2. 靜態(tài)編譯BusyBox 1.26.2并比較大小
使用gcc 6.3, armel, musl 1.1.16: 183348 字節(jié)
使用gcc 6.3, armel, uclibc-ng 1.0.22 : 210620 字節(jié)
使用gcc 6.2, armel, glibc: 755088 字節(jié)
3.2 指令集選擇
編譯rootfs進(jìn)行測(cè)試對(duì)比:
用gcc 7.4編譯,生成ARM代碼:
根文件系統(tǒng)總大?。?.79 MB
用gcc 7.4編譯,生成Thumb2代碼:
根文件系統(tǒng)總大?。?.10 MB(-18%)
性能方面:Thumb2的性能明顯改善(大約少于5%,但是從一次運(yùn)行到另一次運(yùn)行,測(cè)量的執(zhí)行時(shí)間略有變化)。
4. 應(yīng)用軟件優(yōu)化
4.1 測(cè)量strace
strace允許跟蹤應(yīng)用程序及其子級(jí)進(jìn)行的所有系統(tǒng)調(diào)用。對(duì)于開(kāi)發(fā)非常有用:
了解如何在用戶空間上花費(fèi)時(shí)間
例如,輕松查找打開(kāi)嘗試(open()),文件訪問(wèn)(read() /write() )和內(nèi)存分配(mmap2() )。無(wú)需訪問(wèn)源代碼即可完成!
尋找耗時(shí)最長(zhǎng)的開(kāi)銷應(yīng)用
查找在應(yīng)用程序和腳本中完成的不必要的工作。例如:多次打開(kāi)同一文件,或嘗試打開(kāi)不存在的文件。
局限性:您無(wú)法跟蹤init進(jìn)程!
關(guān)于strace 參見(jiàn)
http://sourceforge.net/projects/strace/:
在所有GNU / Linux系統(tǒng)上可用,可以由您的交叉編譯工具鏈生成器構(gòu)建。
更簡(jiǎn)單的辦法:直接拷貝一個(gè)現(xiàn)成的靜態(tài)二進(jìn)制文件。 參見(jiàn)
https://github.com/bootlin/staTIc-binaries/tree/master/strace
可以查看進(jìn)程的操作情況:
1. 訪問(wèn)文件,分配內(nèi)存。。.
2. 通常足以發(fā)現(xiàn)簡(jiǎn)單的錯(cuò)誤。
用法:
1.strace 《命令》(開(kāi)始一個(gè)新進(jìn)程)
2.strace -p 《pid》(跟蹤現(xiàn)有進(jìn)程)strace -c 《command》(統(tǒng)計(jì)進(jìn)程的系統(tǒng)開(kāi)銷時(shí)間)
如查看cat操作:
4.2 Linux上的性能監(jiān)測(cè)工具oprofile
Oprofile是linux上的性能監(jiān)測(cè)工具:
具有兩種工作方式:legacy模式和perf_events模式
legacy模式:
1.精度低,請(qǐng)使用內(nèi)核驅(qū)動(dòng)程序進(jìn)行配置
2.使用CONFIG_OPROFILE進(jìn)行編譯配置
3.用戶空間工具:opcontrol和oprofiled
perf_events模式:
1.使用硬件性能計(jì)數(shù)器
2.使用CONFIG_PERF_EVENTS和CONFIG_HW_PERF_EVENTS編譯配置
3.用戶空間工具:operf
其使用方法:
legacy 模式:
opcontrol --vmlinux=/path/to/vmlinux # opTIonal step
opcontrol --start
/my/command
opcontrol --stop
perf_events 模式
operf --vmlinux=/path/to/vmlinux /my/command
利用opreport獲取結(jié)果
4.3 perf工具
Perf 是內(nèi)置于Linux 內(nèi)核源碼樹(shù)中的性能剖析(profiling)工具。它基于事件采樣原理,以性能事件為基礎(chǔ),支持針對(duì)處理器相關(guān)性能指標(biāo)與操作系統(tǒng)相關(guān)性能指標(biāo)的性能剖析??捎糜谛阅芷款i的查找與熱點(diǎn)代碼的定位。linux2.6及后續(xù)版本都自帶該工具,幾乎能夠處理所有與性能相關(guān)的事件
使用硬件性能計(jì)數(shù)器
使用CONFIG_PERF_EVENTS和CONFIG_HW_PERF_EVENTS進(jìn)行配置
用戶空間工具:性能。它是內(nèi)核源代碼的一部分,因此始終與您的內(nèi)核同步。
用法:perf record /my/command
通過(guò)以下方式獲得結(jié)果:perf report
4.4 連接器優(yōu)化
啟動(dòng)時(shí)使用的應(yīng)用程序組代碼:
查找啟動(dòng)期間調(diào)用的功能,例如使用
-finstrument-funcTIons gcc選項(xiàng)。
創(chuàng)建一個(gè)自定義的鏈接描述文件,以按調(diào)用順序重新排列這些函數(shù)??梢酝ㄟ^(guò)將每個(gè)函數(shù)放在各自的部分中來(lái)實(shí)現(xiàn):
-ffuncTIon-sections gcc選項(xiàng)。
特別對(duì)于具有較大MTD讀取塊的閃存存儲(chǔ)特別有用。因?yàn)樽x取整個(gè)讀取塊后,極有可能讀取不必要的數(shù)據(jù)。
詳細(xì)信息:http://blogs.linux.ie/caolan/2007/04/24/controlling-symbol-ordering/
通過(guò)如下方法,可以找到有望被優(yōu)化的地方:
1.啟動(dòng)一次應(yīng)用程序并測(cè)量其啟動(dòng)時(shí)間。
2.再次啟動(dòng)應(yīng)用程序并測(cè)量其啟動(dòng)時(shí)間。由于它的代碼應(yīng)仍在Linux文件緩存中,故其代碼加載時(shí)間將為零。
從而知道第一次加載應(yīng)用程序代碼(及其庫(kù))所花費(fèi)的時(shí)間。鏈接器優(yōu)化節(jié)省的時(shí)間應(yīng)少于此上限。
然后據(jù)此可以決定是否有必要這樣對(duì)該應(yīng)用進(jìn)行鏈接優(yōu)化。由于鏈接優(yōu)化必須修改應(yīng)用程序的編譯方式,因此此類優(yōu)化的成本很高。
4.4.1 Prelink 預(yù)鏈接工具
Prelink是Red Hat 開(kāi)發(fā)者 Jakub Jelinek 所設(shè)計(jì)的工具,正如其名字所示,Prelink利用事先鏈接代替運(yùn)行時(shí)鏈接的方法來(lái)加速共享庫(kù)的加載,它不僅可以加快起動(dòng)速度,還可以減少部分內(nèi)存開(kāi)銷,是各種Linux架構(gòu)上用于減少程序加載時(shí)間、縮短系統(tǒng)啟動(dòng)時(shí)間和加快應(yīng)用程序啟動(dòng)的很受歡迎的一個(gè)工具。
預(yù)鏈接減少了啟動(dòng)可執(zhí)行文件所需的時(shí)間
在Android上廣泛使用
必須配置為知道哪些庫(kù)需要進(jìn)行預(yù)鏈接,并將為每個(gè)可用符號(hào)分配一個(gè)固定的地址,從而消除了在啟動(dòng)可執(zhí)行文件時(shí)重新定位符號(hào)的需要。
請(qǐng)注意安全性,因?yàn)榭蓤?zhí)行代碼始終加載在同一地址。
代碼以及文檔參見(jiàn)
http://people.redhat.com/jakub/prelink/
支持ARM,但自2013年以來(lái)未發(fā)布。Buildroot也不支持。但是,x86比較容易實(shí)現(xiàn)。
工具鏈/應(yīng)用程序優(yōu)化的部分就在此結(jié)束了,下篇我們將繼續(xù)講嵌入式Linux啟動(dòng)時(shí)間優(yōu)化的方法之文件系統(tǒng),請(qǐng)大家繼續(xù)關(guān)注我們電子發(fā)燒友網(wǎng)和嵌入式客棧。