dailyLearning -- 程序啟動(dòng)原理

艾拉

一個(gè) iOS App 的 main 函數(shù)位于 main.m 中妓忍,這是我們熟知的程序入口仇哆。但對(duì) objc 了解更多之后發(fā)現(xiàn),程序在進(jìn)入我們的 main 函數(shù)前已經(jīng)執(zhí)行了很多代碼拷呆,比如熟知的動(dòng)態(tài)庫(kù)的加載, runtime 的初始化, + load 方法等臀稚。本文將跟隨程序執(zhí)行順序,刨根問(wèn)底肋联,從 dyld 到 runtime 威蕉,看看程序從啟動(dòng)到 main 函數(shù)執(zhí)行之前都發(fā)生了什么。

main之前的加載過(guò)程
  • Mach-O 加載
  • 從 dyld 開始
    1. 加載程序相關(guān)依賴庫(kù), 并對(duì)這些庫(kù)進(jìn)行鏈接;
    2. 調(diào)用每個(gè)依賴庫(kù)的初始化方法橄仍,在這一步韧涨,runtime被初始化;
    3. 將程序依賴的動(dòng)態(tài)鏈接庫(kù)遞歸加載進(jìn)內(nèi)存;
    4. runtime會(huì)對(duì)項(xiàng)目中所有類進(jìn)行類結(jié)構(gòu)初始化,然后調(diào)用所有的load方法;
    5. dyld返回main函數(shù)地址沙兰,main函數(shù)被調(diào)用;
  • main函數(shù)

Mach-O 加載

關(guān)于 Mach-O 文件這里 有一篇 文章介紹 Mach-O 文件.
Mach-O文件格式是 OS X 與 iOS 系統(tǒng)上的可執(zhí)行文件格式氓奈,像我們編譯過(guò)程產(chǎn)生的 .O 文件翘魄,以及程序的可執(zhí)行文件鼎天,動(dòng)態(tài)庫(kù)等都是Mach-O文件。它的結(jié)構(gòu)如下:

mach-o文件

有如下幾個(gè)部分組成:

Header:保存了一些基本信息暑竟,包括了該文件運(yùn)行的平臺(tái)斋射、文件類型、LoadCommands的個(gè)數(shù)等等但荤。
LoadCommands:可以理解為加載命令罗岖,在加載Mach-O文件時(shí)會(huì)使用這里的數(shù)據(jù)來(lái)確定內(nèi)存的分布以及相關(guān)的加載命令。比如我們的main函數(shù)的加載地址腹躁,程序所需的dyld的文件路徑桑包,以及相關(guān)依賴庫(kù)的文件路徑。
Data: 這里包含了具體的代碼纺非、數(shù)據(jù)等等哑了。

系統(tǒng)加載程序可執(zhí)行文件后,通過(guò)分析文件來(lái)獲得dyld所在路徑來(lái)加載dyld烧颖,然后就將后面的事情甩給 dyld 了弱左。

從 dyld 開始

  • dyld: (the dynamic link editor)動(dòng)態(tài)鏈接器,其源碼是開源的炕淮。
    dyld::_main函數(shù)源碼

    一切源于 dyldStartup.s 這個(gè)文件拆火,其中用匯編實(shí)現(xiàn)了名為 __dyld_start 的方法,匯編太生澀,它主要干了兩件事:

調(diào)用 dyldbootstrap::start() 方法(省去參數(shù))
上個(gè)方法返回了main 函數(shù)地址们镜,填入?yún)?shù)并調(diào)用main 函數(shù)
這個(gè)步驟隨手就能驗(yàn)證出來(lái)币叹,設(shè)置一個(gè)符號(hào)斷點(diǎn)斷在_objc_init:


這個(gè)函數(shù)是runtime的初始化函數(shù),后面會(huì)提到憎账。程序運(yùn)行在很早的時(shí)候斷住套硼,這時(shí)候看調(diào)用棧:

看到了棧底的 dyldbootstrap::start() 方法,繼而調(diào)用了 dyld::_main() 方法胞皱,其中完成了剛才說(shuō)的遞歸加載動(dòng)態(tài)庫(kù)過(guò)程邪意,由于 libSystem 默認(rèn)引入,棧中出現(xiàn)了 libSystem_initializer 的初始化方法反砌。

  • ImageLoader: 用于輔助加載特定可執(zhí)行文件格式的類雾鬼,程序中對(duì)應(yīng)實(shí)例可簡(jiǎn)稱為image(如程序可執(zhí)行文件,F(xiàn)ramework庫(kù)宴树,bundle文件)策菜。

兩步走:

  1. 在程序運(yùn)行時(shí)它先將動(dòng)態(tài)鏈接的 image 遞歸加載 (也就是上面測(cè)試棧中一串的遞歸調(diào)用的時(shí)刻);
  2. 再?gòu)目蓤?zhí)行文件 image 遞歸加載所有符號(hào);

當(dāng)然所有這些都發(fā)生在我們真正的 main 函數(shù)執(zhí)行前。

  • runtime 與 +load
    剛才說(shuō)到 libSystem 是若干個(gè)系統(tǒng) lib 的集合酒贬,所以它只是一個(gè)容器 lib 而已又憨,而且它也是開源的,里面實(shí)質(zhì)上就一個(gè)文件锭吨,init.c蠢莺,由 libSystem_initializer 逐步調(diào)用到了 _objc_init,這里就是 objc 和 runtime 的初始化入口零如。

除了 runtime 環(huán)境的初始化外躏将,_objc_init中綁定了新 image 被加載后的 callback:

dyld_register_image_state_change_handler(
dyld_image_state_bound, 1, &map_images);
dyld_register_image_state_change_handler(
dyld_image_state_dependents_initialized, 0, &load_images);

可見 dyld 擔(dān)當(dāng)了 runtime 和 ImageLoader 中間的協(xié)調(diào)者,當(dāng)新 image 加載進(jìn)來(lái)后交由 runtime 去解析這個(gè)二進(jìn)制文件的符號(hào)表和代碼考蕾。繼續(xù)上面的斷點(diǎn)法祸憋,斷住神秘的 +load 函數(shù):

清楚的看到整個(gè)調(diào)用棧和順序:

dyld開始將程序二進(jìn)制文件初始化
交由ImageLoader讀取image,其中包含了我們的類肖卧、方法等各種符號(hào)
由于runtimedyld綁定了回調(diào)蚯窥,當(dāng) image加載到內(nèi)存后,dyld會(huì)通知 runtime 進(jìn)行處理
runtime接手后調(diào)用map_images 做解析和處理塞帐,接下來(lái) load_images 中調(diào)用call_load_methods方法拦赠,遍歷所有加載進(jìn)來(lái)的Class,按繼承層級(jí)依次調(diào)用Class+load方法和其Category+load方法
至此壁榕,可執(zhí)行文件中和動(dòng)態(tài)庫(kù)所有的符號(hào)(Class矛紫,Protocol,Selector牌里,IMP颊咬,…)都已經(jīng)按格式成功加載到內(nèi)存中务甥,被runtime所管理,再這之后喳篇,runtime 的那些方法(動(dòng)態(tài)添加Class敞临、swizzle等等才能生效)

main函數(shù)

當(dāng)所有的依賴庫(kù)庫(kù)的 lnitializer 都調(diào)用完后,dyld::main函數(shù)會(huì)返回程序的 main 函數(shù)地址麸澜,main 函數(shù)被調(diào)用挺尿,從而代碼來(lái)到了我們熟悉的程序入口。

結(jié)語(yǔ)

  • 整個(gè)事件由 dyld 主導(dǎo)炊邦,完成運(yùn)行環(huán)境的初始化后编矾,配合 ImageLoader 將二進(jìn)制文件按格式加載到內(nèi)存,
  • 動(dòng)態(tài)鏈接依賴庫(kù)馁害,并由 runtime 負(fù)責(zé)加載成 objc 定義的結(jié)構(gòu)窄俏,所有初始化工作結(jié)束后,dyld 調(diào)用真正的 main 函數(shù)碘菜。
    這里只是簡(jiǎn)單了概括了從程序啟動(dòng)->dyld加載依賴庫(kù)->runtime初始化->main 的過(guò)程凹蜈。

參考:
iOS 程序 main 函數(shù)之前發(fā)生了什么
iOS程序啟動(dòng)->dyld加載->runtime初始化(初識(shí))

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市忍啸,隨后出現(xiàn)的幾起案子仰坦,更是在濱河造成了極大的恐慌,老刑警劉巖计雌,帶你破解...
    沈念sama閱讀 216,324評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件悄晃,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡白粉,警方通過(guò)查閱死者的電腦和手機(jī)传泊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門鼠渺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)鸭巴,“玉大人,你說(shuō)我怎么就攤上這事拦盹【樽妫” “怎么了?”我有些...
    開封第一講書人閱讀 162,328評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵普舆,是天一觀的道長(zhǎng)恬口。 經(jīng)常有香客問(wèn)我,道長(zhǎng)沼侣,這世上最難降的妖魔是什么祖能? 我笑而不...
    開封第一講書人閱讀 58,147評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮蛾洛,結(jié)果婚禮上养铸,老公的妹妹穿的比我還像新娘雁芙。我一直安慰自己,他們只是感情好钞螟,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評(píng)論 6 388
  • 文/花漫 我一把揭開白布兔甘。 她就那樣靜靜地躺著,像睡著了一般鳞滨。 火紅的嫁衣襯著肌膚如雪洞焙。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,115評(píng)論 1 296
  • 那天拯啦,我揣著相機(jī)與錄音澡匪,去河邊找鬼。 笑死褒链,一個(gè)胖子當(dāng)著我的面吹牛仙蛉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播碱蒙,決...
    沈念sama閱讀 40,025評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼荠瘪,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了赛惩?” 一聲冷哼從身側(cè)響起哀墓,我...
    開封第一講書人閱讀 38,867評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎喷兼,沒想到半個(gè)月后篮绰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,307評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡季惯,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評(píng)論 2 332
  • 正文 我和宋清朗相戀三年吠各,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片勉抓。...
    茶點(diǎn)故事閱讀 39,688評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡贾漏,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出藕筋,到底是詐尸還是另有隱情纵散,我是刑警寧澤,帶...
    沈念sama閱讀 35,409評(píng)論 5 343
  • 正文 年R本政府宣布隐圾,位于F島的核電站伍掀,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏暇藏。R本人自食惡果不足惜蜜笤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望盐碱。 院中可真熱鬧把兔,春花似錦啊胶、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至聘惦,卻和暖如春某饰,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背善绎。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工黔漂, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人禀酱。 一個(gè)月前我還...
    沈念sama閱讀 47,685評(píng)論 2 368
  • 正文 我出身青樓炬守,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親剂跟。 傳聞我的和親對(duì)象是個(gè)殘疾皇子减途,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評(píng)論 2 353