內(nèi)核架構(gòu)
內(nèi)核的基礎(chǔ)知識
所有現(xiàn)代的操作系統(tǒng)在設(shè)計師都包含一個稱為內(nèi)核(kernel)的組件喇聊,是整個系統(tǒng)的核心。內(nèi)核就是操作系統(tǒng)。從高層次看睡榆,所有運行的應(yīng)用程序?qū)嶋H上是內(nèi)核的“客戶”场航,而內(nèi)核向客戶提供各種服務(wù)缠导,即系統(tǒng)調(diào)用(system call)。
內(nèi)核也是一個調(diào)度器旗闽。所有現(xiàn)在的操作系統(tǒng)都是搶占式的多任務(wù)(preemptive multitasking)系統(tǒng)酬核,而實際上,程序的數(shù)目可以遠多于處理器(或核心)的數(shù)目适室。內(nèi)核必須判斷哪一個程序(或進程嫡意、或線程)運行在哪一個處理器/核心上。
內(nèi)核提供的另一組服務(wù)就是安全服務(wù)(security service)捣辆,用戶最容易感受到的安全服務(wù)是許可(permission)和權(quán)限(right)蔬螟,這些機制可以保證系統(tǒng)中各種資源的完整性、隱私性以及公平實用汽畴。
內(nèi)核架構(gòu)
巨內(nèi)核
巨內(nèi)核(monolithic旧巾,也稱為宏內(nèi)核、單內(nèi)核)架構(gòu)是“經(jīng)典”的內(nèi)核架構(gòu)忍些,而且仍然是UNIX和Linux 世界采用的主要內(nèi)核架構(gòu)鲁猩。巨內(nèi)核采取的方式是將所有的內(nèi)核功能:不論是基礎(chǔ)功能還是高級功能,全部放在一個地址空間中罢坝。在這個架構(gòu)的內(nèi)核中廓握,線程調(diào)度和內(nèi)存管理,以及文件系統(tǒng)嘁酿、安全管理隙券、甚至設(shè)備驅(qū)動都在一起。Linux的內(nèi)核實現(xiàn)非常接近于標準的UN*X 內(nèi)核闹司,其架構(gòu)圖如下:
所有的內(nèi)核功能都實現(xiàn)在同一個地址空間中娱仔。為了進一步優(yōu)化,巨內(nèi)核不僅將所有的功能都組織在同一個地址中游桩,還將這個地址映射到每一個進程的內(nèi)存中牲迫。在巨內(nèi)核架構(gòu)中,從用戶態(tài)到內(nèi)核態(tài)的切換非常高效借卧,基本上就是一次線程切換的開銷恩溅,內(nèi)核的內(nèi)存頁面映射在所有進程的地址空間中,也就是說谓娃,除非硬件強制將內(nèi)核態(tài)和用戶態(tài)隔離外脚乡,兩者之間其實沒有任何分別。所有的進程,不論所有者或者功能奶稠,都包含一份內(nèi)核內(nèi)存的拷貝俯艰,就好像包含共享庫的拷貝一樣。此外锌订,這些拷貝都映射到了同一組物理頁面竹握,而且是常駐內(nèi)存的物理頁面。這樣不僅結(jié)束了寶貴的RAM辆飘,而且避免了系統(tǒng)調(diào)用時產(chǎn)生重大開銷啦辐,這一點尤其重要,使得系統(tǒng)調(diào)用好用戶態(tài)融為一體蜈项。
微內(nèi)核
盡管微內(nèi)核(microkernel)不是那么常見芹关,但是微內(nèi)核卻是我們要關(guān)注的重點。因為XNU的核心組件Mach就是一個微內(nèi)核系統(tǒng)紧卒。
一個微內(nèi)核只包含最核心的功能侥衬,代碼也是最精簡的。內(nèi)核值復(fù)制完成最最關(guān)鍵的部分:任務(wù)調(diào)度跑芳、內(nèi)存管理轴总,其他功能都交給外部服務(wù)程序(通常是用戶態(tài))完成。服務(wù)程序之間完全被隔離開博个,服務(wù)程序之間的所有通訊都是由消息傳遞(message passing)完成怀樟。消息傳遞機制允許將消息(通常是透明的)以及后續(xù)消息投遞至服務(wù)程序的隊列中排隊,服務(wù)程序可以從隊列中取出消息并且依次處理盆佣。微內(nèi)核架構(gòu)如下圖:
微內(nèi)核的優(yōu)點:
- 正確性
- 穩(wěn)定性和健壯性
- 靈活性
微內(nèi)核的缺點:
- 性能差
混合內(nèi)核
混合內(nèi)核(hybrid kernel)試圖結(jié)合兩種內(nèi)核(巨內(nèi)核和微內(nèi)核)的好處往堡。內(nèi)核的最核心部分支持底層服務(wù),包括調(diào)度罪塔、進程間通信(inter-process cimmunication,IPC)和虛擬內(nèi)存养葵,是自包含的征堪,這一部分像微內(nèi)核一樣。所有其他的服務(wù)都實現(xiàn)在這個核心者外关拒,但是也在內(nèi)核態(tài)中佃蚜,而且和這個核心在同一個內(nèi)存空間中。
OS X 的 XNU 是一個混合內(nèi)核着绊,XNU 的核心 Mach 最早是一個真正的微內(nèi)核谐算,現(xiàn)在Mach 的原語仍然是圍繞著消息傳遞的基礎(chǔ)構(gòu)建的。然而归露,消息通常是以指針形式傳遞的洲脂,因此沒有昂貴的復(fù)制操作。這是因為大部分服務(wù)現(xiàn)在都在同一個地址空間中執(zhí)行(因此也被歸為巨內(nèi)核)剧包。類似的恐锦,建立在 Mach 之上的BSD層一直都是巨內(nèi)核往果,而且這個子系統(tǒng)也在同一個地址空間中。
用戶態(tài)和內(nèi)核態(tài)
Intel 架構(gòu):ring
基于Intel的系統(tǒng)提供了所需要的基于硬件的分離一铅。從286處理器開始陕贮,Intel 引入了“保護模式”的概念(在386中得到了極大的增強)。保護模式強制使用了4個“ring”潘飘。這些“ring0”指的就是權(quán)限級別肮之,分別從0到3編號。這些“ring”以同心圓的方式組織卜录,最內(nèi)層的ring為 ring 0戈擒,最外層的為ring 3。 ring 0的權(quán)限最高暴凑,通常被稱為超級用戶模式(Supervisor mode)峦甩。只有最受信任的代碼才能運行在處理器的ring 0上。隨著ring級別遞增现喳,安全限制越多凯傲,權(quán)限也越低。
ring 0對應(yīng)的是內(nèi)核態(tài)嗦篱,ring 3對應(yīng)的是用戶態(tài)冰单。 ring 1 和 ring 2 預(yù)留給操作系統(tǒng)服務(wù)使用,但是在實際中卻沒有使用灸促。ring 編號較小的代碼可以隨意切換到編號更大的ring诫欠,但是決不允許編號更大的ring切換到編號更小的ring,除非編號較小的ring已經(jīng)建立好了一個調(diào)用門(call gate)機制浴栽。
ARM架構(gòu):CPSR
ARM 處理器使用了一個特殊的寄存器:當前程序狀態(tài)寄存器(current program status register荒叼,CPSR)來定義處理器所在的模式,ARM 處理器有以下主要的操作模式典鸡,如下表:
模式|表示模式的位|用途
---|---|
USR|10000|用戶模式 --- 不允許操作
SVC|10011|管理器模式(默認的內(nèi)核模式)
SYS|11111|系統(tǒng)模式 --- 同用戶模式被廓,但是允許寫入CPSR
FIQ|10001|快速中斷請求
IRQ|10010|普通中斷請求
ABT|10111|終止模式 --- 錯誤的內(nèi)存訪問
UND|11011|未定義模式 --- 非法/不支持的指令
USR 是唯一沒有特權(quán)的模式,在內(nèi)核通常運行組SVC模式萝玷。在任何特權(quán)模式中嫁乘,都可以直接訪問CPSR寄存器,隱藏只要修改CPSR中的模式位即可切換模式球碉。在用戶態(tài)蜓斧,必須使用一種用戶態(tài)/內(nèi)核態(tài)轉(zhuǎn)換機制。
內(nèi)核態(tài)/用戶態(tài)轉(zhuǎn)換機制
用戶態(tài)和內(nèi)核態(tài)的轉(zhuǎn)換機制有兩種類型:
- 自愿轉(zhuǎn)換:當應(yīng)用程序要求內(nèi)核服務(wù)的時候睁冬,應(yīng)用程序可以進行一個調(diào)用進入內(nèi)核態(tài)挎春,通過一個預(yù)定義的硬件指令可以開始進入內(nèi)核態(tài)的切換。這些內(nèi)核服務(wù)稱為系統(tǒng)調(diào)用
- 非自愿轉(zhuǎn)換:當發(fā)生執(zhí)行異常、中斷或處理器陷阱的時候搂蜓,代碼的執(zhí)行會被掛起狼荞,并且保留發(fā)生錯誤時候的完整狀態(tài)“锱觯控制權(quán)被轉(zhuǎn)交給預(yù)定義的內(nèi)核態(tài)錯誤處理程序或中斷服務(wù)程序(interrupt service rountine相味,ISR)
Intel 上的陷阱處理程序
** 異常:陷阱/錯誤/終止**
在Intel 架構(gòu)上,中斷向量的前20個單元定義為異常(exception)殉挽;異常指的是處理器在執(zhí)行代碼的時候可能碰到的所有非正常狀況丰涉。異常的情況分為以下3種錯誤(fault):指令遇到一個可以糾正的異常,并且處理器可以重新啟動這條出現(xiàn)異常的指令斯碌,這種異常稱為錯誤一死。一個常見的例子是頁錯誤,當某個虛擬內(nèi)存地址表示的頁面不足物理RAM中是發(fā)生頁錯誤傻唾。出現(xiàn)錯誤的時候投慈,執(zhí)行錯誤處理程序,完成之后返回到生成這個錯誤的指令冠骄。
陷阱(trap):類似于錯誤伪煤,但是錯誤處理完成后返回發(fā)送陷阱指令之后的那條指令。
中止(abort):不可重啟指令凛辣。
中斷
中斷是由 CPU 中的一個特殊組件產(chǎn)生的抱既。這個組件稱為可編程中斷控制器(Programmable Interrupt Controller,PIC)扁誓,在更加高級的CPU中防泵,稱之為高級可編程中斷控制器(Advanced PIC,APIC)蝗敢。PIC 接收來自系統(tǒng)總線上的設(shè)備的消息捷泞,然后將消息分揀到某一條中斷請求(Interrupt Request,IRQ)線上去寿谴。當產(chǎn)生中斷的時候锁右,PIC將相應(yīng)的中斷標記為活躍。在這個中斷被一個函數(shù)(稱為中斷處理程序或中斷服務(wù)程序)處理或服務(wù)完成之前拭卿,這條中斷線一直保持活躍狀態(tài)骡湖。處理這個中斷的函數(shù)要負責(zé)重置這條線的狀態(tài)贱纠。
以前的PIC(稱為XT-PIC)只要16條中斷線峻厚,范圍為0~15。而現(xiàn)代的APIC 允許多大255條這樣的中斷線谆焊。如果需要的話惠桃,IRQ線可以被多個設(shè)備共享。
一般性的經(jīng)驗法則,只要滿足一下條件辜王,中斷就會被分發(fā)出去:對應(yīng)的中斷請求線當前不忙(中斷線忙說明前一個中斷還沒處理完)或被屏蔽(被屏蔽說明處理器或處理器核心忽略這條中斷線)
沒有編號更低的中斷線狀態(tài)為忙
本地CPU/處理器核心沒有(通過底層CLI/STI匯編指令)禁用所有的中斷
在Intel 架構(gòu)上XNU對陷阱和中斷的處理
XNU實際上將Intel 的異常統(tǒng)一稱為“陷阱”劈狐。大部分操作系統(tǒng)內(nèi)核都不會為每一個陷阱設(shè)置獨立的處理程序,而是為所有的陷阱設(shè)置一個處理程序呐馆,然后這個處理程序通過switch( ) 進行不同的處理肥缔,或者根據(jù)預(yù)定義的表跳轉(zhuǎn)到不同的函數(shù)。XNU的做法也是如此汹来,定義了TRAP宏和USER_TRAP宏续膳,這些宏通過其他有一些宏(IDT_ENTRY_WRAPPER和PUSH_FUNCTION)設(shè)置棧。ARM上陷阱處理程序
ARM的架構(gòu)比Intel 簡單多了收班。在ARM的角度看坟岔,任何非用戶態(tài)都是通過一個異常或者一個中斷進入的摔桦。因此系統(tǒng)調(diào)用是利用SVC指令通過模擬的中斷完成的社付。SVC 是“SuperVisor Call”的簡稱,過去成SWI邻耕,即SoftWare Interrupt(軟件中斷)鸥咖,其實過去的這個名稱更為準確:當時這個指令執(zhí)行的時候,CPU 自動將控制權(quán)轉(zhuǎn)交給及其的陷阱向量赊豌,在陷阱向量中有一個預(yù)定義的內(nèi)核指令正在等待扛或,通常是分支跳轉(zhuǎn)到某個具體處理程序的指令。
內(nèi)核要負責(zé)設(shè)置好CPU 支持的所有模式的陷阱處理程序碘饼。iOS 的內(nèi)核要依照下表列出的陷阱處理程序設(shè)置ExceptionVectorsBase熙兔,從而完成陷阱處理程序的設(shè)置。
偏移量 | 異常 | 處理程序 |
---|---|---|
0x00 | 重置 | _fleh_reset |
0x04 | 未定義指令 | _fleh_undef |
0x08 | 軟件中斷 | _fleh_swi |
0x0c | 預(yù)取中止 | _fleh_prefabt |
0x10 | 數(shù)據(jù)終止 | _fleh_dataabt |
0x14 | 地址異常 | _fleh_addrexc |
0x18 | 中斷請求 | _fleh_irq |
0x1c | 快速中斷請求 | _fleh_fiq |
自愿的內(nèi)核轉(zhuǎn)換
當用戶態(tài)的程序需要內(nèi)核服務(wù)的時候艾恼,會發(fā)出一個系統(tǒng)調(diào)用住涉,系統(tǒng)調(diào)用將控制權(quán)轉(zhuǎn)換交給內(nèi)核。實現(xiàn)系統(tǒng)調(diào)用的方法有兩種:
模擬中斷:Intel 架構(gòu)遺留的傳統(tǒng)方法钠绍。 ARM 采用這種方法(通過SVC/SWI指令)
指令:SYSENTRY 和 SYSCALL
ARM上的自愿內(nèi)核轉(zhuǎn)換
ARM架構(gòu)沒有專門的系統(tǒng)調(diào)用指令舆声,仍然是使用系統(tǒng)調(diào)用。內(nèi)核加載的時候會覆蓋所有的陷阱處理柳爽,其中包括軟件中斷(SWI)處理程序媳握。當用戶態(tài)程序執(zhí)行SVC指令的時候,控制權(quán)轉(zhuǎn)交給處理程序fleh_swi磷脯,CPU 進入內(nèi)核模式蛾找。
系統(tǒng)調(diào)用的處理
大部分人都熟悉POSIX系統(tǒng)調(diào)用。然而在XNU中赵誓,POSIX 系統(tǒng)調(diào)用只是4種系統(tǒng)調(diào)用的類別之一:
SYSCALL_CLASS | 處理程序 | 包含的調(diào)用 |
---|---|---|
UNIX | unix_syscall[64] | POSIX/BSD 系統(tǒng)調(diào)用:“經(jīng)典”的系統(tǒng)調(diào)用打毛,是XNU 的BSD API接口 |
MACH | mach_call_munger[64] | Mach 陷阱:直接調(diào)用XNU 的 Mach 核心的接口 |
MDEP | machdep_syscall[64] | 機器的相關(guān)的調(diào)用:用于訪問處理器特定的功能 |
DIAG | diagCall[64] | 診斷調(diào)用:用于底層的內(nèi)核診斷柿赊。通過引導(dǎo)diag啟用 |
POSIX/BSD 系統(tǒng)調(diào)用
POSIX/BSD 的接口是 XNU 暴露出來的主要接口,這些在內(nèi)部稱為“UNIX 系統(tǒng)調(diào)用”或“BSD 調(diào)用”unix_syscall
BSD 系統(tǒng)調(diào)用的流程如下:
(1)驗證傳入的狀態(tài)快照和處理器架構(gòu)是否相匹配
(2)通過current_task 獲得當前BSD 進程的數(shù)據(jù)結(jié)構(gòu)幻枉。檢查這個BSD 進程確實存在
(3)如果系統(tǒng)調(diào)用號為0碰声,那么說明這是一個非直接的系統(tǒng)調(diào)用。相應(yīng)地修正參數(shù)
(4)系統(tǒng)調(diào)用傳遞的參數(shù)應(yīng)該是64位的熬甫。對于64未的處理程序來說胰挑,如果系統(tǒng)調(diào)用的闡述不能全部通過寄存器進行傳遞(即參數(shù)數(shù)目大于6的情況),則需要一些額外的工作椿肩,多余的參數(shù)需要復(fù)制到棧上洽腺。在32位的處理程序中,需要對參數(shù)進行mung操作覆旱。mung指的是從用戶態(tài)復(fù)制參數(shù)的同時保持32位/64位兼容性的過程
(5)執(zhí)行sysent表中的系統(tǒng)調(diào)用
(6)在很罕見的情況下蘸朋,系統(tǒng)調(diào)用可能會表示需要重新執(zhí)行,重新執(zhí)行是由pal_syscall_restart( )處理的
(7)系統(tǒng)調(diào)用返回的錯誤碼放在返回寄存器中(Intel:EAX/RAX 寄存器扣唱;ARM:R0 寄存器)
(8)系統(tǒng)調(diào)用處理通過thread_exception_return( )返回(在iOS 上通過load_and_go_user 返回)藕坯,這個函數(shù)的處理和return_from_trap( )相同,在返回的過程中會處理 ASTsysent
sysent 表維護了 BSD 的系統(tǒng)調(diào)用噪沙。這個表是一個由名稱類似的數(shù)據(jù)結(jié)構(gòu)組成的數(shù)組** Mach 陷阱**
在32位OS X 或 iOS 上系統(tǒng)調(diào)用編號為負炼彪,或者在64位系統(tǒng)上類別為Mach,那么內(nèi)核流程進入Mach陷阱處理的流程正歼,而不是BSD 系統(tǒng)調(diào)用的處理辐马。Mach 陷阱的處理程序成為mach_call_munger[64]mach_call_munger
Mach 陷阱是有mach_mall_munger[64] 處理的。UNIX 和 Mach 調(diào)用的參數(shù)都要進程 mung 處理局义,32 位的 unix_syscall 依然包含 munging 操作的代碼mach_trap_table
mach_trap_table 是一個mach_trap_t 結(jié)構(gòu)體數(shù)組喜爷。之后跟著是mach_syscall_name_table,其中保存了對應(yīng)的名字萄唇。機器相關(guān)的調(diào)用
除了Mach 陷阱和 UNIX 系統(tǒng)調(diào)用外檩帐, XNU 還包含機器相關(guān)的調(diào)用。
這些調(diào)用的主要功能是和CPU 緩存相關(guān)(例如MMU 失效相關(guān)的指令和數(shù)據(jù)緩存相關(guān)的指令)另萤,確實反映了“機器相關(guān)”的本質(zhì)machdep_call_table
機器相關(guān)的調(diào)用也有自己的分發(fā)表:machdep_call_table湃密,大部分機器相關(guān)的調(diào)用在Intel架構(gòu)上沒有用,在32位架構(gòu)上四敞,有設(shè)置LDT和GTD的調(diào)用泛源。在64 位架構(gòu)上,只要一個調(diào)用:tthread_fast_set_cthread_self函數(shù)忿危。這個函數(shù)設(shè)置處理器的控制寄存器c13和c0达箍。在ARM中調(diào)用機器相關(guān)調(diào)用的方法是:將R12 設(shè)置為0x8000 0000,然后在R3 中傳入調(diào)用編號診斷調(diào)用
這一類系統(tǒng)調(diào)用專門用于診斷癌蚁。這一類系統(tǒng)調(diào)用只定義了一個診斷調(diào)用:diagCall/diagCall64幻梯。在Intel 時代,XNU 的diagCall 功能只支持一個診斷代碼:dgRuptStat(#25)努释,用于查詢或重置每一個CPU的中斷統(tǒng)計信息