本文參考《Mac OS X and iOS Internals: To the Apple’s Core》 by Jonathan Levin
文章內(nèi)容主要是閱讀這本書的讀書筆記,建議讀者掌握《操作系統(tǒng)》,了解現(xiàn)代操作系統(tǒng)的技術(shù)特點(diǎn),再閱讀本文可以事半功倍。
雖然iOS系統(tǒng)內(nèi)核使用極簡(jiǎn)的微內(nèi)核架構(gòu),但內(nèi)容依然十分龐大,所以會(huì)分
系統(tǒng)架構(gòu)、進(jìn)程調(diào)度、內(nèi)存管理和文件系統(tǒng)四個(gè)部分進(jìn)行闡述。
操作系統(tǒng)管理所有的硬件資源,操作系統(tǒng)內(nèi)核管理最核心的資源CPU和內(nèi)存。上一篇闡述了Mach通過(guò)進(jìn)程管理CPU,本文主要闡述XNU和Mach如何高效的管理內(nèi)存
1 內(nèi)存分配
- 基于棧的內(nèi)存分配:通常由編譯器處理,因?yàn)闂V刑畛涞耐ǔJ浅绦虻淖詣?dòng)變量
- 基于堆的內(nèi)存分配:用于動(dòng)態(tài)內(nèi)存分配,只限于用戶態(tài)使用,在內(nèi)層面,既沒有用戶對(duì)也沒有棧的存在。
1.1 alloca 棧分配
按照傳統(tǒng),棧一般都是保存自動(dòng)變量,正常情況棧由系統(tǒng)管理,但是在iOS中某些情況下,程序員也可以選擇用棧來(lái)動(dòng)態(tài)分配內(nèi)存,方法是使用鮮為人知的alloca( ) 這個(gè)函數(shù)的原型和malloc( )是一樣的,區(qū)別在于這個(gè)函數(shù)返回的指針是棧上的地址而不是堆中的地址。
從實(shí)現(xiàn)角度,alloca( )從兩方面優(yōu)于malloc( )
- 在棧中非配空間只不過(guò)是簡(jiǎn)單的修改棧指針寄存器,時(shí)間消耗低,不用擔(dān)心頁(yè)面錯(cuò)誤
- 當(dāng)分配空間的函數(shù)返回時(shí),棧中分配的空間會(huì)自動(dòng)釋放,解決內(nèi)存地址泄露問題
但是棧空間通常比堆空間受限很多,所以alloca( )非常適合名稱較短的函數(shù)中對(duì)小空間的分配
1.2 堆分配
堆是由C語(yǔ)言運(yùn)行時(shí)維護(hù)的用戶態(tài)數(shù)據(jù)結(jié)構(gòu),通過(guò)堆的使用,程序可以不用直接在頁(yè)面的層次處理內(nèi)存分配。Darwin的libC 采用了一個(gè)基于分配區(qū)域(allocation zone)的特殊分配算法
2 BSD內(nèi)存管理
在iOS中內(nèi)存的管理是由在Mach層中進(jìn)行的,BSD只是對(duì)Mach接口進(jìn)行了POSIX封裝,方便用戶態(tài)進(jìn)程調(diào)用。
XNU內(nèi)存管理的核心機(jī)制是虛擬內(nèi)存管理,在Mach 層中進(jìn)行的,Mach 控制了分頁(yè)器,并且向用戶態(tài)導(dǎo)出了各種 vm_ 和 mach_vm_ 消息接口。 為方便用戶態(tài)進(jìn)程使用BSD對(duì)Mach 調(diào)用進(jìn)行了封裝,通過(guò)current_map( ) 獲得當(dāng)前的Mach 內(nèi)存映射,最后再調(diào)用底層的Mach 函數(shù)。
2.1 MALLOC 和 zone
BSD 的malloc 系列函數(shù)<bsd/sys/malloc.h> 頭文件中。函數(shù)名為_MALLOC、_FREE、_REALLOC、_MALLOC_ZONE、_FREE_ZONE
2.2 mcache 和 slab 分配器
mcache機(jī)制是BSD 提供的基于緩存的非常高效的內(nèi)存分配方法。默認(rèn)實(shí)現(xiàn)基于mach zone,通過(guò)mach zone提供預(yù)分配好的緩存內(nèi)存。
mcache具有可擴(kuò)展架構(gòu),可以使用任何后端 slab 分配器。
使用mcache 機(jī)制的主要優(yōu)點(diǎn)是速度:內(nèi)存分配和維護(hù)是在每一個(gè) CPU 自有的cache中進(jìn)行的,因此可以映射到CPU的物理cache,從而極大地提升訪問速度。
2.4 內(nèi)存壓力
Mach VM層支持VM pressure 的機(jī)制,這個(gè)機(jī)制是可用RAM量低到危險(xiǎn)程度的處置,下面我們會(huì)詳細(xì)講,這里不展開。
當(dāng)RAM量低到危險(xiǎn)時(shí),Mach的pageout 守護(hù)程序會(huì)查詢一個(gè)進(jìn)程列表,查詢駐留頁(yè)面數(shù),然后向駐留頁(yè)面數(shù)最高的進(jìn)程發(fā)送NOTE_VM_PRESSURE ,會(huì)在進(jìn)程隊(duì)列中發(fā)出一個(gè)事件。被選中的進(jìn)程會(huì)響應(yīng)這個(gè)壓力通知,iOS中的運(yùn)行時(shí)會(huì)調(diào)用 didReceiveMemoryWarning 方法。
然而有些時(shí)候這些操作沒有效果,當(dāng)內(nèi)存壓力機(jī)制失敗之后,** 非常時(shí)間要用非常手段 **, Jetsam機(jī)制介入。
2.3 Jestam/Memorystatus
當(dāng)進(jìn)程不能通過(guò)釋放內(nèi)存緩解內(nèi)存壓力時(shí),Jestam機(jī)制開始介入。這是iOS 實(shí)現(xiàn)的一個(gè)低內(nèi)存清理的處理機(jī)制。也稱為Memorystatus,這個(gè)機(jī)制有點(diǎn)類似于Linux的“Out-of-Memory”殺手,最初的目的就是殺掉消耗太多內(nèi)存的進(jìn)程。Memorystatus維護(hù)了兩個(gè)列表:
- 快照列表:保存系統(tǒng)中所有進(jìn)程的狀態(tài)以及消耗的內(nèi)存頁(yè)面數(shù)
- 優(yōu)先級(jí)列表:保存要?dú)⒌舻膫溥x進(jìn)程
在iOS的用戶態(tài)可以通過(guò) sysctl(2)查詢這些列表,優(yōu)先級(jí)列表可以在用戶態(tài)進(jìn)行設(shè)置。
2.3 進(jìn)程休眠
在iOS 5中,Jestsam/Memorystatus 和默認(rèn)的freezer 結(jié)合在一起,實(shí)現(xiàn)了對(duì)進(jìn)程的冷凍而不是殺死。通過(guò)這種方式可以提供更好的用戶體驗(yàn),因?yàn)閿?shù)據(jù)不會(huì)丟失,而且當(dāng)內(nèi)存情況好轉(zhuǎn)時(shí)進(jìn)程可以安全恢復(fù)。(感謝@易步指出本段錯(cuò)誤)
用戶態(tài)也可以通過(guò)pid_suspend( ) 和 pid_resume( )控制進(jìn)程的休眠。
iOS 定義了 pid_hibernate,通過(guò)發(fā)送kern_hibernation_wakeup信號(hào)喚醒kernel_hibernation_thread 線程,這個(gè)線程專用于對(duì)進(jìn)程冷凍操作。
實(shí)際的進(jìn)程休眠操作是由jestsam_hibernate_top_proc 完成的,這個(gè)函數(shù)通過(guò)task_freeze冷凍底層的任務(wù)。
冷凍操作需要遍歷任務(wù)的vm_map,然后將vm_map 傳遞給默認(rèn)的 freezer。
3 Mach 虛擬內(nèi)存 virtual memory,VM
VM是Darwin系統(tǒng)內(nèi)存管理的核心機(jī)制。
3.1 VM架構(gòu)
VM 機(jī)制主要通過(guò)內(nèi)存對(duì)象(memory object)和分頁(yè)器(pager)的形式管理內(nèi)存。
Mach 虛擬內(nèi)存的實(shí)現(xiàn)非常全面而且通用。這部分由兩個(gè)層次構(gòu)成:一層是和硬件相關(guān)的部分,另一層構(gòu)建在這一層之上和硬件無(wú)關(guān)的公共層。OS X 和 iOS 使用的幾乎一樣的底層機(jī)制,硬件無(wú)關(guān)層以及之上的BSD 層中的機(jī)制都是一樣的。
3.1.1 VM系統(tǒng)全貌
Mach 的 VM子系統(tǒng)可以說(shuō)是和其要管理的內(nèi)存一樣復(fù)雜和充滿了各種細(xì)節(jié)。然后從高層次看,可以看到兩個(gè)層次:
- 虛擬內(nèi)存的層次
- 物理內(nèi)存的層次
3.1.2 虛擬內(nèi)存層
虛擬內(nèi)存這一層完全以一種機(jī)器無(wú)關(guān)的方式來(lái)管理虛擬內(nèi)存。這一層通過(guò)幾種關(guān)鍵的抽象表示虛擬內(nèi)存:
vm_map
表示任務(wù)地址空間內(nèi)的一個(gè)或多個(gè)虛擬內(nèi)存區(qū)域。每一個(gè)區(qū)域都是有一個(gè)獨(dú)立的條目 vm_map_entry 表示。這些條目由一個(gè)雙向鏈表vm_map_links維護(hù)。vm_map_entry
這是關(guān)鍵的數(shù)據(jù)結(jié)構(gòu),盡管只有在包含這個(gè)結(jié)構(gòu)的映射的上下文中才會(huì)訪問到這個(gè)結(jié)構(gòu)。每一個(gè)vm_map_entry 都表示了虛擬內(nèi)存中一塊連續(xù)的區(qū)域(region)。每一個(gè)這樣的區(qū)域都可以通過(guò)指定的訪問保護(hù)權(quán)限進(jìn)行保護(hù)(和虛擬內(nèi)存頁(yè)面采用同樣的權(quán)限)。任務(wù)之間可以共享區(qū)域。vm_map_entry
通常指向一個(gè)vm_object,但是也可以指向一個(gè)嵌套的vm_map,即子映射(submap)。vm_object
用于將vm_map_entry 和實(shí)際支撐的內(nèi)存關(guān)聯(lián)起來(lái)。這個(gè)數(shù)據(jù)結(jié)構(gòu)包含一個(gè)vm_page 的鏈表,還包含一個(gè)用于訪問正確分頁(yè)器的Mach 端口(稱為memory_object),通過(guò)這個(gè)分頁(yè)器進(jìn)行頁(yè)面的獲取或清理操作。vm_page
vm_page 真正表示了vn_object 或部分vm_object(由vm_object中的偏移量表示)。
vm_page 可以有多種狀態(tài):駐留內(nèi)存、交換出、加密、干凈和臟等。
Mach 允許使用多個(gè)分頁(yè)器。事實(shí)上,默認(rèn)就存在3~4個(gè)分頁(yè)器。Mach 的分頁(yè)器以外部實(shí)體的形式存在:是專業(yè)的任務(wù),有點(diǎn)類似于其他系統(tǒng)上的內(nèi)核交換(kernel-swapping)線程。Mach 的設(shè)計(jì)允許分頁(yè)器和內(nèi)核任務(wù)隔離開,設(shè)置允許用戶態(tài)任務(wù)作為分頁(yè)器。類似地,底層的后備存儲(chǔ)也可以駐留在磁盤交換文件中(通過(guò)OS X 中的 default_pager 處理),可以映射到一個(gè)文件(由vnode_pager處理),可以是一個(gè)設(shè)備(由device_pager 處理)。注意:在Mach 中,每一個(gè)分頁(yè)器處理的都是屬于這個(gè)分頁(yè)器的頁(yè)面的請(qǐng)求,但是這些請(qǐng)求必須通過(guò)pageout 守護(hù)程序發(fā)出。這些守護(hù)程序(實(shí)際上就是內(nèi)核線程)維護(hù)內(nèi)核的頁(yè)面表,并且判定哪些頁(yè)面需要被清除出去。因此,這些守護(hù)程序維護(hù)的分頁(yè)策略和分頁(yè)器實(shí)現(xiàn)的分頁(yè)操作是分開的。
3.1.3 物理內(nèi)存層
物理內(nèi)存的頁(yè)面處理的是虛擬內(nèi)存到物理內(nèi)存的映射,因?yàn)樘摂M內(nèi)存中的內(nèi)容最終總要存儲(chǔ)在某個(gè)地方。這一層面只有一個(gè)抽象,那就是pmap,不過(guò)這個(gè)抽象非常重要,因?yàn)樘峁┝藱C(jī)器無(wú)關(guān)的接口。這個(gè)接口隱藏了底層平臺(tái)的細(xì)節(jié),底層的細(xì)節(jié)需要在處理器層次進(jìn)行分頁(yè)操作,其中要處理的對(duì)象包括硬件頁(yè)表項(xiàng)(page table entry,PTE)、翻譯查找表(translation lookaside buffer,TLB)等。
每一個(gè)Mach 任務(wù)都要自己的虛擬內(nèi)存空間,任務(wù)的struct task 中的 map 字段保存的就是這個(gè)虛擬內(nèi)存空間。
vm_page_entry 中最關(guān)鍵的元素是vm_map_object,這是一個(gè)聯(lián)合體,既可以包含另一個(gè)vm_map(作為子映射),也可以包含一個(gè)vm_object_t(由于這是一個(gè)聯(lián)合體,所以具體的內(nèi)容需要用布爾字段is_sub_map 來(lái)判斷)。vm_object 是一個(gè)巨大的數(shù)據(jù)結(jié)構(gòu),其中包含了處理底層虛擬內(nèi)存所需要的所有數(shù)據(jù)。vm_object的數(shù)據(jù)結(jié)構(gòu)中的大部分字段都是用位表示的標(biāo)志。這些字段表示了底層的內(nèi)存狀態(tài)(聯(lián)動(dòng)、物理連續(xù)和持久化等狀態(tài))和一些計(jì)數(shù)器(引用計(jì)數(shù)、駐留計(jì)數(shù)和聯(lián)動(dòng)計(jì)數(shù)等)。不過(guò)有3個(gè)字段需要特別注意:
memq:vm_page 對(duì)象的鏈表,每一項(xiàng)都表示一個(gè)駐留內(nèi)存的虛擬內(nèi)存頁(yè)面。盡管一個(gè)對(duì)象可以表示一個(gè)單獨(dú)的頁(yè)面,但是多數(shù)情況下一個(gè)對(duì)象可以包含多個(gè)頁(yè)面,所以每一個(gè)頁(yè)面關(guān)聯(lián)到一個(gè)對(duì)象時(shí)都會(huì)有一個(gè)偏移值
page:memory_object 對(duì)象,這是指向分頁(yè)器的Mach 端口。分頁(yè)器將未駐留內(nèi)存的頁(yè)面關(guān)聯(lián)到后備存儲(chǔ),后備存儲(chǔ)可以是內(nèi)存映射的文件、設(shè)備和交換文件,后備存儲(chǔ)保存了沒有駐留內(nèi)存的頁(yè)面。換句話說(shuō),分頁(yè)器(可以有多個(gè))負(fù)責(zé)將數(shù)據(jù)從后備存儲(chǔ)移入內(nèi)存以及將數(shù)據(jù)從內(nèi)存移出到后備存儲(chǔ)。分頁(yè)器對(duì)于虛擬內(nèi)存子系統(tǒng)來(lái)說(shuō)極為重要
internal:vm_page 中眾多標(biāo)志位之一,如果這個(gè)位為真,那么表示這個(gè)對(duì)象是由內(nèi)核內(nèi)部使用的。這個(gè)標(biāo)志位的值決定了對(duì)象中的頁(yè)面會(huì)進(jìn)入哪一個(gè)pageout隊(duì)列
3.2 Mach 物理內(nèi)存管理
盡管內(nèi)核和用戶空間一樣,基本上只在虛擬地址空間內(nèi)操作,但是虛擬內(nèi)存最終還是要翻譯為物理地址的。機(jī)器的RAM 實(shí)際上是虛擬內(nèi)存中開的窗口,允許程序訪問虛擬內(nèi)存是有限的,而且通常是不連續(xù)的區(qū)域,這些區(qū)域的上線就是機(jī)器上安裝的內(nèi)存。而虛擬內(nèi)存中其他部分則要么延遲分配,要么共享,要么被交換到外部存儲(chǔ)中,外部存儲(chǔ)通常是磁盤。
然而虛擬內(nèi)存和具體的底層架構(gòu)相關(guān)。盡管虛擬內(nèi)存和物理內(nèi)存的概念在所有架構(gòu)上本周都是一樣的,但是具體的實(shí)現(xiàn)細(xì)節(jié)則各有千秋。XNU 構(gòu)建與Mach 的物理內(nèi)存抽象層之上,這個(gè)的抽象層成為pmap。pmap 從設(shè)計(jì)上對(duì)物理內(nèi)存提供了一個(gè)統(tǒng)一的接口,屏蔽了架構(gòu)相關(guān)的區(qū)別。這對(duì)于XNU來(lái)說(shuō)非常有用,因?yàn)閄NU支持的物理內(nèi)存的架構(gòu)包括以前的PowerPC,現(xiàn)在主要是Intel,然后在iOS 中還支持ARM。
3.2.1 pmap 的 API
Mach 的pmap 層邏輯上由一下兩個(gè)子層構(gòu)成:
- 機(jī)器無(wú)關(guān)層
提供了一組基本上和及其無(wú)關(guān)的API。只要求及其支持基本的虛擬內(nèi)存分頁(yè)的概念。VM層只考慮pamp_t 并傳遞這個(gè)類型的數(shù)據(jù)即可,pmap_t 是一個(gè)指向struct pmap 是指針,實(shí)際上是一個(gè)void 指針 - 機(jī)器相關(guān)層
將pmap綁定到一個(gè)具體的實(shí)現(xiàn),處理底層敬愛個(gè)的各種細(xì)節(jié)
3.3 Mach Zone
Mach Zone的概念相當(dāng)于Linux的內(nèi)存緩存(memory cache)和Windows 的Pool。Zone 是一種內(nèi)存區(qū)域,用于快速分配和是否頻繁使用的固定大小的對(duì)象。Zone的API是內(nèi)核內(nèi)部使用的,在用戶態(tài)不能訪問。Mach中Zone的使用非常廣泛。
3.3.1 Mach Zone 的結(jié)構(gòu)
所有的zone 內(nèi)存實(shí)際上都是在調(diào)用zinit( )時(shí)預(yù)先分配好的(zinit( )通過(guò)底層內(nèi)存分配器kernel_memory_allocate( )分配內(nèi)存)zalloc( )實(shí)際上是對(duì)REMOVE_FROM_ZONE 宏的封裝,作用是返回zone的空閑列表中的下一個(gè)元素(如果zone已滿,則調(diào)用kernel_memory_allocate( )分配這個(gè)zone在定義的alloc_size字節(jié))。zfree( ) 使用的是相反功能的宏 ADD_TO_ZONE。這兩個(gè)函數(shù)都會(huì)執(zhí)行合理數(shù)量的參數(shù)檢查,不過(guò)這些檢查幫助不大:過(guò)去zone分配相關(guān)的bug已經(jīng)導(dǎo)致了數(shù)據(jù)可以被黑客利用的內(nèi)存損壞。zalloc( ) 最重要的客戶是內(nèi)核中的kalloc( ),這個(gè)函數(shù)從kalloc.*系列zone中分配內(nèi)存。BSD的mcache機(jī)制也會(huì)從自己的zone中分配內(nèi)存。BSD內(nèi)核zone也是如此,BSD內(nèi)核zone直接構(gòu)建與Mach的zone之上。
3.5 Mach 分頁(yè)器
進(jìn)程的內(nèi)存需求早晚會(huì)超過(guò)可用的RAM,系統(tǒng)必須有一種方法能夠?qū)⒉换顒?dòng)的頁(yè)面?zhèn)浞萜饋?lái),并且從RAM中刪除,騰出更多的RAM給活動(dòng)的頁(yè)面使用,至少暫時(shí)能夠滿足活動(dòng)頁(yè)面的需求。在其他操作系統(tǒng)中,這個(gè)工作專門是由專門的內(nèi)核線程完成的。在Mach 中,這些專門的任務(wù)稱為分頁(yè)器(pager),分頁(yè)器可以是內(nèi)核線程,設(shè)置建議是外部的用戶態(tài)服務(wù)程序。
Mach分頁(yè)器是一個(gè)內(nèi)存管理器,負(fù)責(zé)將虛擬內(nèi)存?zhèn)浞莸侥硞€(gè)特定類型的后備存儲(chǔ)中。當(dāng)內(nèi)存容量不足,內(nèi)存頁(yè)面需要被交換出內(nèi)存是,后備存儲(chǔ)保存內(nèi)存頁(yè)面的內(nèi)容:當(dāng)換出的內(nèi)存頁(yè)面需要被使用時(shí),將內(nèi)存的頁(yè)面恢復(fù)到RAM中。只有“臟”頁(yè)面才需要進(jìn)行上述的換出和換入,因?yàn)椤芭K”頁(yè)面是在內(nèi)存中修改過(guò)的頁(yè)面,要從RAM中剔除時(shí)必須保存到磁盤中防止數(shù)據(jù)丟失。
要注意的是,這里提到的分頁(yè)器僅僅實(shí)現(xiàn)了各自負(fù)責(zé)的內(nèi)存對(duì)象的分頁(yè)操作,這些分頁(yè)器不會(huì)控制系統(tǒng)的分頁(yè)策略。分頁(yè)策略是有vm_pageout 守護(hù)線程負(fù)責(zé)的。
3.4.1 分頁(yè)器的類型
iOS 和 OS X 中XNU 包含的分頁(yè)器種類都是一樣的。下表是XNU中的內(nèi)存分頁(yè)器的多種類型:
| 內(nèi)存分頁(yè)器 | 用途 |
|---|---|
| Default | 負(fù)責(zé)內(nèi)存交換的通用接口 |
| VNode | 內(nèi)存映射文件 |
| Device | 概念類似VNode,通過(guò)IO接口將數(shù)據(jù)交換給外設(shè) |
| Swapfile | 阻止直接映射交換文件的企圖 |
| Apple-protected | 實(shí)現(xiàn)Apple代碼加密機(jī)制 |
| Default Freezer | 在物理內(nèi)存較少切沒有真正交換文件的系統(tǒng)上,應(yīng)用程序在后臺(tái)時(shí)不需要真正的運(yùn)行,當(dāng)用戶將應(yīng)用切入后臺(tái),系統(tǒng)將應(yīng)用冷凍,在應(yīng)用恢復(fù)時(shí)解凍 |
3.6 分頁(yè)策略管理
3.6.1 Pageout 守護(hù)程序
pageout 守護(hù)程序其實(shí)不是一個(gè)真的守護(hù)程序,而是一個(gè)線程。而且不是一般的線程:當(dāng)kernel_bootstarp_thread( ) 完成內(nèi)核初始化工作并且沒有其他事情可做時(shí),就調(diào)用vm_pageout( ) 成為了pageour 守護(hù)程序, vm_pageout( ) 永遠(yuǎn)不返回。這個(gè)線程管理頁(yè)面交換的策略,判斷哪些頁(yè)面需要寫回到其后備存儲(chǔ)。
vm_pageout線程
vm_pageout( ) 函數(shù)講kernel_bootstrap_thread 線程轉(zhuǎn)變?yōu)閜ageout 守護(hù)程序,這個(gè)函數(shù)實(shí)際上重新設(shè)置了這個(gè)線程。設(shè)置完成后,調(diào)用vm_pageout_continute( ),這個(gè)函數(shù)周期性地喚醒并執(zhí)行vm_page_scan( ),維護(hù)4個(gè)頁(yè)面表(稱為頁(yè)面隊(duì)列)。系統(tǒng)中的每一個(gè)vm_page 都通過(guò)pageq字段綁定這4個(gè)隊(duì)列中的一個(gè):
vm_page_queue_active
最近活躍且駐留在內(nèi)存中的頁(yè)面vm_page_queue_inactive
最近不活躍的頁(yè)面,因此這些頁(yè)面是頁(yè)面換出的備選頁(yè)面。根據(jù)這些頁(yè)面的使用情況,可能會(huì)被換出,也可能會(huì)被重新激活vm_page_queue_free
空閑頁(yè)面表。這些頁(yè)面曾經(jīng)是非活躍的頁(yè)面,但是被清理出去了(頁(yè)面換出)vm_page_queue_speculative
這些頁(yè)面是通過(guò)預(yù)讀策略投機(jī)映射的頁(yè)面,這些頁(yè)面是不活躍的,但是很可能很快會(huì)變?yōu)榛钴S頁(yè)面
垃圾回收線程
垃圾回收線程(vm_pageout_garbage_collect( ))偶爾會(huì)被vm_pageout_scan( ) 通過(guò)其續(xù)體喚醒。垃圾回收機(jī)制線程處理4個(gè)方面的垃圾回收工作:
srack_collect( )
內(nèi)核棧中的頁(yè)面consider_machine_collect( )
回收機(jī)器相關(guān)的頁(yè)面consider_buffer_cache_collect( )
如果確定定義了這個(gè)函數(shù)則調(diào)用這個(gè)函數(shù)。調(diào)用這通過(guò)vm_set_buffer_cleanup_callout( ) 定義這個(gè)函數(shù)。BSD 層在bufinit( ) 函數(shù)中注冊(cè)了buffer_cache_gc( ) 函數(shù)consider_zone_gc( )
zone 相關(guān)的垃圾回收
3.6.2 處理頁(yè)錯(cuò)誤
vm_pageout( ) 守護(hù)程序處理的只是交換的一個(gè)方向,從物理內(nèi)存換出到后備存儲(chǔ)。而另外一個(gè)方向是頁(yè)面換入,則是發(fā)生在頁(yè)面錯(cuò)誤的時(shí)候處理的。這個(gè)邏輯非常復(fù)雜,簡(jiǎn)化為一下步驟:
- 如果陷阱的原因是頁(yè)錯(cuò)誤,那么機(jī)器級(jí)別的線程處理程序調(diào)用vm_fault( )
- vm_fault( ) 函數(shù)調(diào)用 vm_pageout_fault( )處理實(shí)際發(fā)生錯(cuò)誤的頁(yè)面,并且從后備存儲(chǔ)中將這個(gè)頁(yè)面返回
- PMAP_ENTER( ) 將頁(yè)面插入任務(wù)的pmap
頁(yè)錯(cuò)誤有很多種,上述只是其中一種,其他類型的也錯(cuò)誤還包括:
- 非法訪問
訪問應(yīng)該沒有映射到進(jìn)程地址空間(即任務(wù)的vm_map)的地址。解引用應(yīng)該野指針時(shí)通常會(huì)發(fā)生這種錯(cuò)誤。發(fā)生這種錯(cuò)誤時(shí)進(jìn)程會(huì)收到SIGSEGV信號(hào) - 頁(yè)面保護(hù)錯(cuò)誤
訪問應(yīng)該映射的地址,但是頁(yè)面的保護(hù)掩碼拒絕請(qǐng)求的訪問 - 寫時(shí)復(fù)制(copy-on-write)
頁(yè)面可以被標(biāo)記可讀,因此如果任務(wù)試圖寫入頁(yè)面時(shí),會(huì)捕捉到這個(gè)錯(cuò)誤,在重新嘗試寫入操作之前可以將這個(gè)頁(yè)面復(fù)制出來(lái)
總結(jié)
VM系統(tǒng)是Mach中最重要最復(fù)雜而且最不好理解的子系統(tǒng)。Mach的內(nèi)存管理核心是分頁(yè)器,分頁(yè)器允許將虛擬內(nèi)存擴(kuò)展到各種后背存儲(chǔ)介質(zhì)上:交換文件、內(nèi)存映射文件、設(shè)備、甚至遠(yuǎn)程主機(jī)。
iOS中提高內(nèi)存使用率的Freezer,以及處理內(nèi)存耗盡的pageout守護(hù)程序。