iOS底層探索之dyld(上):動態連接器流程分析

1. 前言

如今的互聯網行業,是一天比一天卷,除了底層是必考點了,還有關於APP的性能優化也是面試常問的點。 太難了web

在優化以前必需要對應用程序加載的流程熟悉,那麼本次博文就對dyld進行底層的初步探索分析。面試

2. 程序加載原理

2.1 代碼編譯過程

咱們都知道代碼編寫完成,必須經過編譯器編譯才能變成能夠執行的文件。bootstrap

程序編譯流程

程序的執行,是把可執行的文件,加載到內存中去執行的,這個可執行的文件(Mach-O)的運行必須依賴不少的庫(.a/.lib/.so),庫是可執行的二進制文件,是可以被加載到內存中去的。這些庫,能夠分爲靜態庫動態庫性能優化

2.2 靜態庫和動態庫

  • 靜態庫: 例如.a.framework。靜態庫連接時,會被完整地複製到可執行文件中,被使用到了屢次,就會複製多份,這樣就有多份拷貝很冗餘,連接時間長了,還浪費了內存空間。
  • 動態庫:例如.dylib.framework。動態庫連接時,只會存在一份,並不會複製多份,在內存中共享這一份,系統只加載一次,誰有用到了就去找這一份,減小了程序的體積大小,能夠節省時間和內存空間。

靜態庫和動態庫 靜態庫都好理解,那麼動態庫在程序中是怎麼加載到內存呢?系統是經過怎樣的方式來連接的呢?這就用到了一個工具,也就是博文開頭提到的dyld(the dynamic link editor)動態連接器,markdown

2.3 dyld 動態連接器

dyld是iOS操做系統的一個重要組成部分,在系統內核作好程序準備工做以後,會交由dyld負責餘下的工做。 dyld的做用:加載各個庫,也就是image鏡像文件,由dyld從內存中讀到表中,加載主程序, link連接各個動靜態庫,進行主程序的初始化工做。網絡

程序加載流程

上面這個圖是dyld的加載工做流程圖,圖中簡單的描述了動態庫註冊和動態庫的加載過程,具體分析還得去看底層源碼,那麼接下來就去探索分析。app

3. 初識dyld

3.1 尋找dyld入口

程序的執行咱們都知道是從main函數開始的,那麼dyld在程序的那個階段執行的呢?是在main函數執行前,仍是再main函數執行後呢?這我也不得而知。函數

啊!你這個博主,奇奇怪怪的,你寫的博文,你會不知道啊? 信你個鬼 這個嘛,就得探索探索了!首先建一個工程將main.m改寫以下:工具

__attribute__((constructor)) void JPFunc(){
	 printf("來了老弟 : %s \n",__func__);
}

int main(int argc, char * argv[]) {
	NSString * appDelegateClassName;
	@autoreleasepool {
		NSLog(@"這是main函數打印");
	    // Setup code that might create autoreleased objects goes here.
	    appDelegateClassName = NSStringFromClass([AppDelegate class]);
	}
	return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}
複製代碼

而後運行程序,打印結果以下:oop

來了老弟 : JPFunc 
dyld初探[37212:516752] 這是main函數打印
複製代碼

這個__attribute__((constructor))是在main函數以前執行的一個函數。

  • 補充:GNU C 的一大特點就是__attribute__ 機制。__attribute__ 能夠設置函數屬性(Function Attribute )、變量屬性(Variable Attribute )和類型屬性(Type Attribute )。

  • __attribute__ 書寫特徵是:__attribute__ 先後都有兩個下劃線,並切後面會緊跟一對原括弧,括弧裏面是相應的__attribute__ 參數。

  • __attribute__ 語法格式爲:__attribute__ ((attribute-list))

main函數執行以前確實是能夠執行其餘函數的,那麼dyld到如今好像尚未相關線索,那麼繼續往下探索。 在main函數打上斷點

斷點斷在main函數上

斷點斷在main函數上,發如今main以前還調用了一個start方法。

libdyld.dylib`start

點開start是一個libdyld.dylib start,忽然想起網絡上很流行的一句話,歡迎來到德萊聯盟libdyld.dylib和這發音好像,哈哈!

可是經過對start下符號斷點,斷不住它。說明這不是入口的地方,咱們知道還有一個方法+load,這個是在main以前必會調用的方法,那麼就能夠在Viewcontroller的寫下+load方法添加斷點,運行程序。在控制檯輸入指令bt,查看調用堆棧信息:

堆棧信息

堆棧信息是一個棧結構,先進後出,因此最底下打印的就是最早執行的。因此如今咱們已經找到dyld的入口了。

3.2 獲取dyld源碼

_dyld_start,那麼這就涉及到底層源碼了,去蘋果開放的源碼官網opensource看看dyld源碼

dyld源碼

咱們研究源碼,就得去看最新的蘋果源碼,畢竟技術更新迭代很快,最新的纔是最流行的,也是最香的,研究起來纔有味道,dyld最新的版本是dyld-852,這部分源碼是不能編譯的,可是並不能妨礙咱們去探索它。那麼咱們如今就去打開dyld這個牛逼的源碼工程一探究竟吧!

3.3 初探dyld 源碼

_dyld_start源碼

全局搜索_dyld_start,發現又是彙編,是否是要瘋了啊!

莫慌靚仔,穩住,不會彙編沒有關係,請耐心往下看!

dyldbootstrap::start

在彙編裏面發現了一個重要方法,dyldbootstrap::start,從紅框中的註釋能夠知道,會調用dyldbootstrap::start這個C++函數,那麼就能夠去全局搜索下,看看C++函數的命名空間。

namespace dyldbootstrap

從命名空間裏面,咱們能夠找到start

在這裏插入圖片描述

start函數裏面返回的是dyld::_main,這就nice了,忽然就很熟悉,很親切。

博文篇幅有限,本篇到此結束! 請看下篇iOS底層探索之dyld(下):動態連接器流程源碼分析

4. 總結

  • 應用程序加載到內存中運行,會經過dyld連接各類庫
  • 庫分爲動態庫靜態庫
  • 靜態庫有多份拷貝,增長包的大小,程序加載連接時間長,浪費了內存空間。
  • 動態庫存在一份,並不會複製多份,節省程序加載連接時間和內存空間
  • iOS的可執行的文件是Mach-O
  • dyld源碼能夠去蘋果的開源opensource查看

更多內容持續更新

🌹 喜歡就點個贊吧👍🌹

🌹 以爲學習到了的,能夠來一波,收藏+關注,評論 + 轉發,以避免你下次找不到我😁🌹

🌹歡迎你們留言交流,批評指正,互相學習😁,提高自我🌹

相關文章
相關標籤/搜索