I/OKit驅(qū)動(dòng)程序框架
和其他操作系統(tǒng)不同,XNU的獨(dú)特之處在于為設(shè)備驅(qū)動(dòng)程序提供了一個(gè)完整的運(yùn)行時(shí)環(huán)境。XNU的設(shè)備驅(qū)動(dòng)程序運(yùn)行時(shí)環(huán)境稱為I/O Kit,是一個(gè)蘋果開發(fā)的一個(gè)私有組件。I/O Kit為開發(fā)者提供了面向?qū)ο蟮膹?qiáng)大能力,主要包括子類化和方法重寫,通過(guò)這些特性使得設(shè)備驅(qū)動(dòng)程序的開發(fā)過(guò)程非常高效。
I/O Kit 還提供了一組用戶態(tài)的API:I/O Kit Framework,這個(gè)框架提供了一些更高級(jí)的特性,例如內(nèi)核態(tài)通知以及內(nèi)核態(tài)和用戶態(tài)之間的雙向通信。
I/O Kit 簡(jiǎn)介
I/OKit 的設(shè)計(jì)非常獨(dú)特。盡管所有的操作系統(tǒng)肯定都有設(shè)備驅(qū)動(dòng)程序,但是大部分都是完全用C語(yǔ)言編寫的,沒(méi)有自己的運(yùn)行時(shí)環(huán)境。但是沒(méi)有一個(gè)能夠像I/OKit 這樣提供面向?qū)ο蟮拈_發(fā)環(huán)境。
I/OKit 是什么
一套幾乎自包含的編程環(huán)境:I/OKit 提供了一些對(duì)于Mach API的封裝的API,這些API用于線程創(chuàng)建、內(nèi)存分配和一些通用任務(wù)
一個(gè)面向?qū)ο蟮拈_發(fā)環(huán)境:I/OKit 驅(qū)動(dòng)程序是從繼承于OSObject類實(shí)例化來(lái)的。開發(fā)者可以找到和自己要寫的驅(qū)動(dòng)程序最接近的那個(gè)類,然后從這個(gè)類開始實(shí)現(xiàn)自己的類,有效地重用這個(gè)類的通用部分的代碼
一個(gè)特別為驅(qū)動(dòng)程序而設(shè)計(jì)的開發(fā)環(huán)境:I/O Kit 為很多操作設(shè)備特別要考慮的問(wèn)題提供了支持,主要包括即插即用和電源管理;另一個(gè)重要的架構(gòu)思想是驅(qū)動(dòng)程序堆疊(driver layering),允許為一個(gè)設(shè)備驅(qū)動(dòng)構(gòu)建在另一個(gè)設(shè)備驅(qū)動(dòng)的基礎(chǔ)之上
** 一個(gè)工作循環(huán)驅(qū)動(dòng)的開發(fā)環(huán)境**:I/O Kit 提供了一個(gè)工作循環(huán)(work loop)模型,工作循環(huán)是一個(gè)不斷處理事件的消息循環(huán)。通常使用工作循環(huán)可以極大簡(jiǎn)化了并發(fā)問(wèn)題,而且通??梢员苊鈱?duì)鎖的使用,鎖是會(huì)影響性能的
一個(gè)基于注冊(cè)表的開發(fā)環(huán)境:I/O Kit 中的所有東西都是要記賬的:對(duì)象需要引用計(jì)數(shù),類需要注冊(cè)等,所有這些信息都通過(guò)I/O Registry(I/O 注冊(cè)表)管理。I/O Kit Registry 是一個(gè)多層的層次化數(shù)據(jù)庫(kù),跟蹤對(duì)象以及對(duì)象之間的相互關(guān)系
一個(gè)用戶態(tài)友好的開發(fā)環(huán)境:I/O Kit 提供了用戶態(tài)可以訪問(wèn)的API,而且完全由用戶態(tài)實(shí)現(xiàn)一些驅(qū)動(dòng)程序,例如USB 設(shè)備的驅(qū)動(dòng)程序就是完全在用戶態(tài)實(shí)現(xiàn)的。I/O Kit 注冊(cè)表也可以在用戶態(tài)訪問(wèn),因此用戶態(tài)可以硬件配置和參數(shù)信息
一個(gè)通過(guò)C++子集實(shí)現(xiàn)的開發(fā)環(huán)境:I/O Kit 是基于C++的,所以I/OKit 利用了一些C++語(yǔ)言有用的編譯時(shí)特性,例如:
名稱空間:I/O Kit 驅(qū)動(dòng)程序可以通過(guò)C++名稱空間將自己的函數(shù)和符號(hào)包裝起來(lái),避免內(nèi)核中全局的符號(hào)突出
名稱重整(name mangling ):I/O Kit 的符號(hào)名稱是經(jīng)過(guò)重新整過(guò)的,其中在函數(shù)名稱中包含了C++層面的原型信息(名稱空間、返回值和參數(shù))
I/OKit 不是什么
一個(gè)完整的C++開發(fā)環(huán)境:I/O Kit 沒(méi)有使用以下特性:
模版
異常
標(biāo)準(zhǔn)的構(gòu)造函數(shù)
一套全功能的API:由于沒(méi)有完整的C++運(yùn)行時(shí)庫(kù),所以所有的運(yùn)行時(shí)庫(kù)功能都是通過(guò)libkern庫(kù)提供的。為了完整地兼容I/O Kit,開發(fā)者應(yīng)該僅僅使用libkern中的API
最靈活的編程模型:I/O Kit 驅(qū)動(dòng)程序必須實(shí)現(xiàn)一個(gè)非常具體的生命周期,因此和其它操作系統(tǒng)中眾所周知的驅(qū)動(dòng)程序回調(diào)函數(shù)有嚴(yán)重的不同。驅(qū)動(dòng)程序的生命周期非常復(fù)雜,開發(fā)者要清楚在什么條件下必須實(shí)現(xiàn)什么回調(diào)函數(shù)
代碼可以搞定一切:I/OKit 驅(qū)動(dòng)程序不僅僅是二進(jìn)制程序。作為內(nèi)核擴(kuò)展,還必須包含一個(gè)Info.plist文件。由于是I/O Kit 驅(qū)動(dòng)程序,所以Info.plist文件必須包含I/O Kit相關(guān)的指令,如果沒(méi)有這些指令的話,驅(qū)動(dòng)程序就無(wú)法正常工作
libkern:I/O Kit的基類
libkern C++運(yùn)行時(shí)是I/OKit的基礎(chǔ),定義了所有I/O Kit驅(qū)動(dòng)程序都可以使用的基礎(chǔ)類。libkern中的基礎(chǔ)類和CoreFundation中的一些功能上有交集,這些基礎(chǔ)類定義在XNU的libkern/libkern/c++目錄中,實(shí)現(xiàn)在libkern/c++中,一個(gè)類對(duì)應(yīng)一個(gè)文件。這個(gè)目錄下還包含了其它支持文件(OSRuntime.c和OSRuntineSupport.cpp),里面包含了libkern初始化的代碼,以及序列化函數(shù)(OSSerialize/OSUnserialize)的代碼。序列化/反序列化操作作用于向/從XML屬性列表中寫入/讀取對(duì)象。
OSObject
I/OKit 中所有類都會(huì)追溯到一個(gè)祖宗,那就是OSObject。
OSMetaClass
I/O Kit 并不支持標(biāo)準(zhǔn)C++的Run Time Type Identifation(RTTI)。但是I/O Kit 提供了一個(gè)同樣強(qiáng)大的機(jī)制:OSMetaClass類。這是一個(gè)抽象類,不能直接使用。需要通過(guò)一些特殊的宏來(lái)實(shí)現(xiàn)RTTI的特性。這些宏包括:
OSDeclareDefaultStructors:用于生產(chǎn)I/O Kit 對(duì)象默默認(rèn)構(gòu)造和析構(gòu)函數(shù)的原型。
OSDefinwMetaClassAndStructors:類似地,這個(gè)宏用在類的實(shí)現(xiàn)中。抽象類使用OsDefineMetaClassAndStructors:這兩個(gè)類都可以添加WithInit后綴,得到的還是包含初始化函數(shù)的宏。
I/O Kit Registry
I/O Kit 維護(hù)了一個(gè)保存了所有對(duì)象及對(duì)象間關(guān)系最新信息的數(shù)據(jù)庫(kù)。這個(gè)數(shù)據(jù)庫(kù)在內(nèi)存中,出納崗位I/O Registry 。 I/O Registry 是一個(gè)多平面的數(shù)據(jù)庫(kù)。簡(jiǎn)單地說(shuō),I/O Registry 表示的是三維的關(guān)系。
IORegistryEntry
I/O RegistryEntry 類是I/O Registry 中所有對(duì)象的父類。I/O RegistryEntry 是一個(gè)包含了對(duì)象屬性的簡(jiǎn)單容器,對(duì)象的屬性保存了一個(gè)OSDictionary 對(duì)象中。這個(gè)類不應(yīng)該直接繼承。I/O Kit 對(duì)象的父類是IOService,即IORegistryEntry的子類。通過(guò)繼承關(guān)系,所有的驅(qū)動(dòng)程序也自動(dòng)注冊(cè)了。
IOService
IOService是IORegistryEntry的直接后代也是唯一后代。IOService也是所有驅(qū)動(dòng)程序()包括蘋果提供的驅(qū)動(dòng)程序以及第三方的驅(qū)動(dòng)程序)的祖先,盡管大部分驅(qū)動(dòng)程序不是直接繼承于IOService,但是IOService仍然是這些驅(qū)動(dòng)程序的最終祖先。這些驅(qū)動(dòng)程序從IOSevice繼承了一組可以直接使用的方法(例如電源管理和中斷處理等),在有些情況下還需要實(shí)現(xiàn)一些方法(例如驅(qū)動(dòng)程序標(biāo)準(zhǔn)的回調(diào)函數(shù))。
用戶態(tài)的I/O Kit
I/O Kit 可以通過(guò)IOKit.Framework提供的API以及IOKitLib API 和 用戶態(tài)通信。這個(gè)框架就是給用戶態(tài)使用的。內(nèi)核態(tài)的I/O Kit組件使用的是Kernel.Framework的IOKit/子目錄。用戶態(tài)應(yīng)用程序可以通過(guò)這些API和內(nèi)核中的I/O Kit 驅(qū)動(dòng)程序以及I/O Kit組件(最重要的是I/O Registry)本身做接口。
所有的I/O Kit 都依賴一個(gè)特殊的主機(jī)端口,I/O Kit 通過(guò)IOMasterPort( )獲得并引用這個(gè)端口。用戶態(tài)和I/O Kit內(nèi)核組件以及驅(qū)動(dòng)程序之間的通信是通過(guò)Mach 消息進(jìn)行的。
訪問(wèn)I/O Registry
有了Master Port,應(yīng)用程序就可以無(wú)限量地發(fā)送I/O Kit 請(qǐng)求了。通常情況下有很多請(qǐng)求都涉及查詢I/O Reigstry。
獲得/設(shè)置驅(qū)動(dòng)程序?qū)傩?/h5>
由于在I/O Kit 模型設(shè)備驅(qū)動(dòng)程序中都是對(duì)象,因此有自己的屬性。這些屬性在用戶態(tài)都是可見(jiàn)的,而且可以通過(guò)用戶態(tài)的客戶程序訪問(wèn)甚至修改。可以通過(guò)以下幾個(gè)函數(shù)進(jìn)行屬性操作:
- IORegistryEntryCreateCFProperties( ):獲取驅(qū)動(dòng)程序完整屬性表的拷貝
- IORegistryEntryCreatepProperty( ):通過(guò)名字訪問(wèn)某一個(gè)屬性
即插即用(通知端口)
用戶態(tài)的應(yīng)用程序有時(shí)會(huì)要求I/O Kit發(fā)出任何I/O Registry 變化的通知,例如設(shè)備到達(dá)(添加)和分離(刪除),以及某些特定設(shè)備的狀態(tài)變化。這個(gè)通知對(duì)于即插即用設(shè)備的支持非常有用,例如插入i-設(shè)備時(shí)啟動(dòng)iTunes。
要請(qǐng)求這些通知,客戶程序必須首先創(chuàng)建一個(gè)通知端口。通過(guò)調(diào)用IONotificationPortCreate 返回的結(jié)果得到IONotificationPort 的引用(即IONotificationPortRef)。這個(gè)引用在用戶態(tài)是不透明的,實(shí)際上隱藏了一個(gè)Mach端口。通知端口可以通過(guò)Mach消息的原語(yǔ)直接監(jiān)聽(tīng),不過(guò)推薦的使用方法是將通知端口連接到一個(gè)運(yùn)行循環(huán)(run loop)結(jié)構(gòu)中。運(yùn)行循環(huán)屬于Core Foundation 編程模型,實(shí)現(xiàn)了消息循環(huán)。當(dāng)消息到達(dá)通知端口時(shí),用戶提供的回調(diào)函數(shù)會(huì)被調(diào)用。
I/O Kit 電源管理
驅(qū)動(dòng)程序可以注冊(cè)電源相關(guān)的通知,既可以響應(yīng)系統(tǒng)電源狀態(tài)的變化,也可以影響系統(tǒng)電源狀態(tài)的變化。在IOPower 平面上可以找到要求這項(xiàng)功能的驅(qū)動(dòng)程序,而且規(guī)模隨著對(duì)電源依賴性的增加而成本增大。應(yīng)用程序也可以請(qǐng)求參與到電源管理當(dāng)中。
I/O Kit 診斷
除了ioreg(8)命令和Xcode 捆綁的IORegistry Explorer外,蘋果僅僅提供了另外兩個(gè)診斷工具:
- ioalloccount(8):顯示I/O Kit 分配的內(nèi)存消耗
- ioclasscount(8):計(jì)算所有注冊(cè)的I/O Kit 類及子類的實(shí)例數(shù),提供的是積累的計(jì)數(shù)
I/O Kit 內(nèi)核驅(qū)動(dòng)程序
I/O Kit 驅(qū)動(dòng)程序是從一個(gè)公共祖先IOService 繼承而來(lái)的對(duì)象。IOService 下面的繼承樹非常龐大復(fù)雜,順著這棵樹下來(lái),驅(qū)動(dòng)程序越來(lái)越具體化,并且只適合驅(qū)動(dòng)程序?qū)S玫脑O(shè)備或總線。I/O Kit的驅(qū)動(dòng)可以分為“驅(qū)動(dòng)程序(driver)”和“節(jié)點(diǎn)(nub)”。節(jié)點(diǎn)簡(jiǎn)單地說(shuō)就是兩個(gè)驅(qū)動(dòng)程序之間的適配器,表示被控制的設(shè)備。
驅(qū)動(dòng)程序匹配
I/O Kit 維護(hù)了一個(gè)Catalogue對(duì)象,這個(gè)對(duì)象是一個(gè)保存了所以已知設(shè)備驅(qū)動(dòng)程序和注冊(cè)的驅(qū)動(dòng)程序personality的數(shù)據(jù)庫(kù)。I/O Kit 利用驅(qū)動(dòng)程序的personality來(lái)匹配驅(qū)動(dòng)程序和新加入的設(shè)備。
IOWorkLoop
I/O Kit 采用了NeXT的runloop模型,用戶態(tài)開發(fā)者應(yīng)該會(huì)想到CFRunLoop。I/O Kit 版本的runloop 成為IOWorkLoop,基本思想是一樣的:提供一個(gè)單線程且現(xiàn)場(chǎng)安全的機(jī)制處理所有類型的時(shí)間,如果不采用這種機(jī)制則是異步的。工作循環(huán)的訪問(wèn)被一個(gè)互斥體保護(hù),因此不需要開率可重入的問(wèn)題以及線程安全的問(wèn)題。
中斷處理
盡管有部分設(shè)備驅(qū)動(dòng)程序是虛擬設(shè)備的驅(qū)動(dòng)程序,但是大部分驅(qū)動(dòng)程序都要面對(duì)真實(shí)的硬件,因此需要和中斷打交道,I/O Kit 想驅(qū)動(dòng)程序開發(fā)者隱藏了Mach 的中斷處理邏輯,I/O Kit 的這項(xiàng)隱藏做得非常棒,開發(fā)者可以很開心地忽略底層細(xì)節(jié)。開發(fā)者不需要陷入中斷處理相關(guān)的具體細(xì)節(jié),I/O Kit 提供了面向?qū)ο蟮闹袛嘁晥D,即高效又直觀。