1.分層抽象,接口與封裝
對于設備驅動程序而言,分離設備業務邏輯和底層硬件操作有利于驅動程序的擴展。而利用面向對象的思想對驅動程序進行抽象是一個很好的方法。其主要的方法就是將驅動程序的業務邏輯中關于底層硬件操作的部分通過某種接口加以規范。如同file_operations,由此便可以使用統一的接口進行業務邏輯相關操作而無需關注硬件底層差異。在系統結構圖上來看相當于將業務邏輯作為上層實現,而具體的硬件操作作為下層依賴,形成不同層級的系統結構。而關于相關接口的實現則交由單獨的平臺相關的程序部分加以實現。此外還可以通過使用簡易工廠模式對提供給業務邏輯對應接口的部分代碼加以進一步封裝。以此實現對于驅動程序代碼的高效封裝,復用和拓展。
(資料圖片僅供參考)
2.次設備號與一類設備
諸如tty設備與磁盤設備等大多數的設備,其硬件實體在系統中往往存在不僅一個實例。驅動程序往往需要維護多個設備,或者說,驅動程序應該維護驅動程序所支持的一類設備。因此,如何在系統中創建一類設備的多個實例并在驅動程序中加以辨識和管理,則是一個必須納入考量的事情。
對于Linux系統而言,每一個設備都有一個主設備號與次設備號,對于一個驅動程序而言,通常會在驅動程序加載的時候注冊設備并獲取主設備號,主設備號用于關聯一組文件IO操作。因此,主設備號便可以用于區分一類設備,而次設備號便可以用于區分一類設備中的不同實例。
例如一個字符設備包含有多個設備實例,可以在注冊設備后,創建多個設備實例。實例代碼如下
3.驅動程序中獲取次設備號
注冊設備時所綁定的一系列文件IO操作是服務于一類設備的,其包含了通用的業務方法抽象。上文指出可以通過次設備號來區分不同的設備實例,因此在接口中如何獲取次設備號則是需要討論的。
在初始化時,通過device_create創建設備時,我們通過MKDEV組合了主設備號與次設備號并用其創建了設備,在/dev目錄中我們也可以發現對于不同的設備,在使用ls打印時會顯示相關的主設備號和次設備號。我們可以發現,設備號信息與設備對象是關聯的。而我們在操作設備的不同實例時恰好就是訪問了不同的設備文件,因此我們來看文件IO接口的原型。
我們可以看到,其每一個接口都至少指定了一個文件作為形參。事實上,Linux內核提供了一個iminor方法用于獲取inode的次設備號。獲取方法如下
而對于諸如open和release方法,其形參列表中存在單獨的inode因此可以直接使用,而對于read,write等方法,則需要從其傳入的file參數中獲取inode信息。Linux內核提供了一個file_inode方法用于提取struct file結構體的inode。
由此便可獲取當前所進行系統調用所訪問的設備文件的次設備號。
拓展
file_inode方法實際上返回了struct file結構體中的f_inode成員。
iminor方法返回了struct inode結構體中的i_rdev成員。
4.驅動程序的分層抽象
我們可以看到,由于多個設備實例的存在,驅動程序需要考慮的情況變得龐大。如果將對于不同設備的底層差異的判斷都交由接口所指定的對應函數實現, 則程序體量會膨脹。并且一旦對硬件進行擴展,便要修改驅動程序的對應源碼。這使得驅動程序的開發維護變得很爛。
一個好的想法在上文中已經提到,我們可以將文件IO接口實現中對于底層的操作封裝為抽象的接口,而由負責硬件的程序部分實現該接口,由此便完成了抽象。
因此,在驅動程序中我們可以將次設備號作為參數傳遞給相應的底層接口,只需要考慮對于這一類設備的業務邏輯即可。大大提高了代碼的復用與易擴展性。
例如,如果我們抽象一個LED的驅動程序,我們可以將LED的操作和具體的硬件實現分離。在對應的IO接口中僅僅實現對業務邏輯的包裝。在接口的實現中實現對業務邏輯的具體實現。
創建兩個文件,led_和led_,編寫Makefile。想要實現將多個c源碼文件編譯成一個內核模塊文件,Makefile編寫如下。
led_負責對字符設備進行注冊,并且實現業務邏輯。
led_實現了f1c100s對于字符設備的底層實現。
想要實現對接口的定義,需要額外編寫一個頭文件led_,用于規范相關的接口。其實現如下
led_
led_
實現如下
led_
該文件主要實現底層的初始化,實現如下
5.測試程序
Copyright @ 2015-2022 中南網版權所有 關于我們 備案號: 浙ICP備2022016517號-4 聯系郵箱:514 676 113@qq.com