動態(tài)鏈接庫加載的具體流程
動態(tài)鏈接庫的加載步驟具體分為5步:
- load dylibs image 讀取庫鏡像文件
- Rebase image
- Bind image
- Objc setup
- initializers
load dylibs image
在每個動態(tài)庫的加載過程中德召, dyld需要:
分析所依賴的動態(tài)庫
找到動態(tài)庫的mach-o文件
打開文件
驗證文件
在系統(tǒng)核心注冊文件簽名
對動態(tài)庫的每一個segment調(diào)用mmap()
通常的,一個App需要加載100到400個dylibs, 但是其中的系統(tǒng)庫被優(yōu)化,可以很快的加載众羡。 針對這一步驟的優(yōu)化有:
減少非系統(tǒng)庫的依賴
合并非系統(tǒng)庫
使用靜態(tài)資源擒悬,比如把代碼加入主程序
rebase/bind
由于ASLR(address space layout randomization)的存在摊唇,可執(zhí)行文件和動態(tài)鏈接庫在虛擬內(nèi)存中的加載地址每次啟動都不固定左医,所以需要這2步來修復鏡像中的資源指針,來指向正確的地址特姐。 rebase修復的是指向當前鏡像內(nèi)部的資源指針晶丘; 而bind指向的是鏡像外部的資源指針。
rebase步驟先進行唐含,需要把鏡像讀入內(nèi)存浅浮,并以page為單位進行加密驗證,保證不會被篡改捷枯,所以這一步的瓶頸在IO滚秩。bind在其后進行,由于要查詢符號表淮捆,來指向跨鏡像的資源郁油,加上在rebase階段,鏡像已被讀入和加密驗證攀痊,所以這一步的瓶頸在于CPU計算桐腌。
通過命令行可以查看相關的資源指針:
xcrun dyldinfo -rebase -bind -lazy_bind myApp.App/myApp
優(yōu)化該階段的關鍵在于減少__DATA segment中的指針數(shù)量。我們可以優(yōu)化的點有:
減少Objc類數(shù)量苟径, 減少selector數(shù)量
減少C++虛函數(shù)數(shù)量
轉(zhuǎn)而使用swift stuct(其實本質(zhì)上就是為了減少符號的數(shù)量)
Objc setup
這一步主要工作是:
注冊Objc類 (class registration)
把category的定義插入方法列表 (category registration)
保證每一個selector唯一 (selctor uniquing)
由于之前2步驟的優(yōu)化案站,這一步實際上沒有什么可做的。
initializers
以上三步屬于靜態(tài)調(diào)整(fix-up)棘街,都是在修改__DATA segment中的內(nèi)容蟆盐,而這里則開始動態(tài)調(diào)整承边,開始在堆和堆棧中寫入內(nèi)容。 在這里的工作有:
Objc的+load()函數(shù)
C++的構(gòu)造函數(shù)屬性函數(shù) 形如attribute((constructor)) void DoSomeInitializationWork()
非基本類型的C++靜態(tài)全局變量的創(chuàng)建(通常是類或結(jié)構(gòu)體)(non-trivial initializer) 比如一個全局靜態(tài)結(jié)構(gòu)體的構(gòu)建舱禽,如果在構(gòu)造函數(shù)中有繁重的工作炒刁,那么會拖慢啟動速度