20135327郭皓--讀書筆記六

第十七章 設備與模塊

在本章中,關於設備驅動和設備管理,咱們討論四種內核成分。node

  • 設備類型:在全部 Unix 系統中爲了統一普通設備的操做所採用的分類.
  • 模塊: Linux 內核中用於按需加載和卸載目標碼的機制.
  • 內核對象:內核數據結構中支持面向對象的簡單操做,還支持維護對象之間的父子關係。 
  • sysfs :表示系統中設備樹的一個文件系統。

17 .1 設備類型

在 Linux 以及全部 Unix 系統中,設備被分爲如下三種類型:linux

  • 塊設備
  • 字符設備
  • 網絡設備 
塊設備一般縮寫爲 blkdev,它是可尋址的,尋址以塊爲單位,塊大小隨設備不一樣而不一樣;塊設備一般支持重定位(seeking)操做,也就是對數據的隨機訪問。 
字符設備一般縮寫爲 cdev,它是不可尋址的,僅提供數據的流式訪問,就是一個個字符,或者一個個字節。
網絡設備最多見的類型有時也以以太網設備(ethemet devices)來稱呼,它提供了對網絡(例如Internet)的訪問,這是經過-個物理適配器(如你的膝上型計算機的 802.11 卡)和一種特定的協議(如 IP協議)進行的。網絡設備打破了Unix的「全部東西都是文件」的設計原則,它不是經過設備節點來訪問,而是經過套接字 API 這樣的特殊接口來訪問。

17.2 模塊

Linux內核是模塊化組成的,它容許內核在運行時動態地向其中插入或從中刪除代碼。這些代碼(包括相關的子例程、數據、函數人口和函數出口)被一併組合在一個單獨的二進制鏡像中,即所謂的可裝載內核模塊中,或簡稱爲模塊。

17.2.1 Hello, World 

hello_ini的函數是模塊的入口點,它經過module_ init()例程註冊到系統中,在內核裝載時被調用。調用module_initO 實際上不是真正的函
數調用,而是一個宏調用,它惟一的參數即是模塊的初始化函數。模塊的全部初始化函數必須符合下面的形式:數組

int my _ init (void) ;

由於init 函數一般不會被外部函數直接調用,因此你沒必要導出該函數,故它可標記爲static類型.網絡

hello_ exit()函數是模塊的出口函數,它由module_exit()例程應冊到系統。退出函數必須符合如下形式:數據結構

void my_exit (void);

與init 函數同樣,你也能夠標記其爲static.MODULE_LICENSE()宏用於指定模塊的版權。若是載入非GPL模塊到系統內存,則會在內核中設置被污染標識一一這個標識只起到記錄信息的做用。框架

 

17.2.2 構建模塊

你的驅動程序是一個釣魚竿和計算機的接口,名爲Fish Master XL 3000模塊化

1. 放在內核派代碼樹中 函數

你須要在 drivers/char/目錄下建 立一個名爲 fishing 的子目錄。接下來須要向drivers/cbar/下的Makefile文件中添加一行。編輯drivers/char/Makefile/並加入:工具

obj -m +• fishing/ 

這行編譯指令告訴模塊構建系統,在編譯模塊時須要進入 fishing/子目錄中。 學習

在drivers的bar/fishing/下,須要添加一個新Makefile 文件,其中須要有下面這行指令:

obj-m +=fishing.o

一切就緒了,此刻構建系統運行將會進入 fishing/ 目錄下,而且將 fishing.c 編譯爲 fishing.ko 模塊.雖然你寫的擴展名是.o,可是模塊被編譯後的擴展名倒是.ko。

2. 放在內核代碼外 

若是你喜歡脫離內核源代碼樹來維護和構建你的模塊,把本身做爲一個圈外入,那你要作的就是在你本身的源代碼樹目錄中創建一個Makefile 文件,它只需事一行指令:

obj -m : = fishing.o 

這條指令就可把 fishing.c 編譯成fishing.ko。 若是你有多個源文件, 那麼用兩行就足夠:

obj -m := fishing.o 
fishing-objs := fishing-main.o fishing-line.o

這樣一來, fisbing-main.c 和 fishing-line.c就一塊兒被編譯和鏈接到 fishing.ko 模塊內了。 

模塊在內核內和在內核外構建的最大區別在於構建過程。 當模塊在內核源代碼樹外圍時,你必須告訴 make 如何找到內核源代碼文件和基礎 Makefile 文件。 不過要完成這個工做一樣不難:

make -c / kernel/source/ location SUBDI RS=$PWD modules 

在這個例子中, kernel/source/location是你配置的內核源代碼樹。 

 

17.2.3 安裝模塊

下面的構建命令用來安裝編譯的模塊到合適的目錄下:

make modules install 

17.2.4 產生模塊依賴性 

多數 Linux發佈版都能自動產 生這些依賴關係信息,並且在每次啓動時更新。若想產生內核依賴關係的信息, root用戶可運行命令:

depmod 

爲了執行更快的更新操做,那麼能夠只爲新模塊生成依賴信息,而不是生成全部的依賴關係,這時root用戶可運行命令:

depmod -A 

17.2.5 戴入模塊

載入模塊最簡單的方法是經過 insmod 命令,這是個功能頗有限的命令,它能傲的就是請求內核載入指定的模塊。 insmod程序不執行任何依賴性分析或進一步的錯誤檢查。它用法簡單, 以 root 身份運行命令:

insmod module.ko 

相似的,卸載一個模塊,你可以使用 rmmod 命令,它一樣須要以 root 身份運行:

rmmod module 

這兩個命令是很簡單,可是它們一點也不智能。先進工具 modprobe 提供了模塊依賴性分析、錯誤智能檢查、錯誤報告以及許多其餘功能和選項。

我強烈建議你們用這個命令。 爲了在內核via modprobe中插入模塊,須要以root身份運行:

modprobe module [ module parameters ] 

modprobe 命令也可用來從內核中卸載模塊,固然這也須要以 root 身份運行:

modprobe -r modules 

17.2.6 管理配置選項

若是你創建了一個新子目錄,並且也但願kconfig 文件存在子該目錄中的話,那麼你必須在一個己存在的kconfig文件中將它引入。你須要加入下面一行指令

source "drivers/char/fishing/Kcor,lfig」

這裏所謂存在的Kconfig 文件多是drivers/char/Kconfig。

配置選項第一行定義了該選項所表明的配置目標。注意CONFIG_ 前綴並不須要寫上。第二行聲明選項類型爲住istate,也就是說能夠編譯進內核( Y ),也可做爲模塊編譯( M),或者乾脆不編譯它( N)。第三行指定了該選項的默認選擇,這裏默認操做是不編譯它。

17.2.7 模塊參數

Linux提供了這樣一個簡單框架一一它可容許驅動程序聲明參數,從而用戶能夠在系統啓動 或者模塊裝載時再指定參數值,這些參數對於驅動程序屬於全局變量。

定義一個模塊參數可經過宏module_param() 完成:

module_param(name, type, perm); 

參數name既是用戶可見的參數名,也是你模塊中存放模塊參數的變量名.參數 type 則存放 了參數的類型,它能夠是byte、 short、 ushort、 int、 uint、 long、 ulong、 charp、bool或invbool, 它們分別表明字節型、短整型、無符號短整形、整型、無符號整型、長整形、無符號長整型;最後一個參數 perm 指定了模塊在 sysfs 文件系統下對應文件的權限,該值能夠是八進制的格式, 好比「44 (全部者 能夠讀寫,組內能夠讀,其餘人能夠讀):或是 S_Ifoo 的定義形式,好比 S_IRUGO I S_IWUSR (任何人可讀, user可寫):若是該值是零,則表示禁止全部的 sysfs項。 

有可能模塊的外部參數名稱不一樣於它對應的內部變量名稱,這時就該使用宏 module_param_ named()定義了:

modul e_param_named(name, variable, type, perm); 

若是須要,也可以使內核直接拷貝字符事到指定的字符數組。module_param_string()完成上述任務:

module_param_string(name, string, len, perm); 

你能夠將內部參數數組命名區別於外部參數,這時你需使用宏: 

module_param_array_named(name, array, type, nump, perm) ; 

最後,你可以使用 MODULE_PARM_DESC()描述你的參數:

static unsigned short size - 1; 
module_param(size, ushort , 0644) ; 
MODULE_PARM_DESC(size ,」The size in inches of the fishing pole. 」); 

17.2.8 導出符號表

在內核中,導出內核函數須要使用特殊的指令: EXPORT_ SYMBOL()和EXPORT_SYMBOL_GPL()。導出的內核函數能夠被模塊調用,而來導出的函數模塊則無陸被調用。

假定get_pirate_ beard_ color() 同時也定義在一個可訪問的頭文件中,那麼如今任何模塊均可以訪問它。有一些開發者但願本身的接口僅僅對GPL-兼容的模塊可見,內核鏈接器使用MODULE_LICENSE()宏可知足這個要求。若是你但願先前的函數僅僅對標記爲GPL 協議的模塊可見,那麼你就須要用:

EXPORT_SYMBOL_GPL(get_pirate_beard_color);

17.3 設備模型

設備模型提供了一個獨立的機制專門來表示設備,並描述其在系統中的拓撲結構,從而使得系統具備如下優勢:

  • 代碼重複最小化.
  • 提供諸如引用計數這樣的統一機制。
  • 能夠列舉系統中全部的設備,觀察它們的狀態,而且查看它們鏈接的總結.
  • 能夠將系統中的所有設備結構以樹的形式完整、有效地展示出來一一包括全部的總統和內部鏈接。
  • 能夠將設備和其對應的驅動聯繫起來,反之亦然。
  • 能夠將設備按照類型加以歸類,好比分類爲輸入設備,而無需理解物理設備的拓撲結構.
  • 能夠沿設備樹的葉子向其根的方向依次遍歷,以保證能以正確順序關閉各設備的電源。

17.3.1 kobject

設備模型的核心部分就是kobject (kernel objt),它自struct kobject 結構體表示,定義於頭文件<linux/kobject>中。sd 指針指向sysfs_ dirent 結構體,該結構體在叨地中表示的就是這kobject. 從sysfs 文件系統內部看,這個結構體是表示kobject的一個inode 結構體.kref提供引用計數。ktype 和kset 結構體對kobject 對象進行描述和分類.

17.3.2 ktype

kobject 對象被關聯到一種特殊的類型,目p ktype (kernel object type 的縮寫)。ktype 由kobj_type 結構體表示,定義於頭文件<linux/kobjt.h>中

ktype 的存在是爲了描述一族kobject 所具備的廣泛特性.如此一來,再也不須要每一個kolct部分別定義本身的特性,而是將這些廣泛的特性在ktype 結構中一次定義,而後全部「同類」的kobject 都能共享同樣的特性。

sysfs_ops 變量指向sysfs_ops 結構體。該結構體描述了sysfs 文件讀寫時的特性。

17.3.3 kset

kset 是kobject 對象的集合體。把它當作是一個容器,可將全部相關的koject 對象,好比「所有的塊設備」置於同一位置。kobject 的kset 指針指向相應的kset 集合。kset 集合由kset 結構體表示,定義於頭文件
<linux/kobject.h>中:

在這個結構中,其中list 鏈接該集合( kset )中全部的kobject 對象, list_lock 是保護這個鏈表中元素的自旋鎖。uevent 就是用戶事件(user event )的縮寫,提供了與用戶空間熱插拔信息進行通訊的機制。

17.3.4 kobject 、ktype 和kset 的相互關係

這裏最重要的傢伙是kobject,它由struct koject 表示。kobject 爲咱們引入了諸如引用計數(reference counting)、父子關係和對象名稱等基本對象道具,而且是以一個統一的方式提供這些功能。不過kobject 自己意義井不大,一般狀況下它須要被嵌入到其餘數據結構中,讓那些包含
它的結構具備了kobject 的特性。

kobject 叉納入了稱做kset 的集合, kset 集合由struct kset 結構體表示。kset 提供了兩個功能。

  • 第一,其中嵌入的kobj創做爲kobject 組的基類。
  • 第二, kset 將相關的kobject 集合在一塊兒。在sysfs 中,這些相關的koject 將以獨立的目錄出如今文件系統中。這些相關的目錄,也許是給定目錄的全部子目錄,它們可能處於同一個kset 。

17.3.5 管理和操做kobject

使用kobjcet 的第一步須要先來聲明和初始化。kobject 經過函數ko均ect_init 進行初始化,該函數定義在文件<linux/kobject.h>中

void kobject_init(struct kobject •kobj, struct kobj_type •ktype);

這個工做每每會在kobject 所在的上層結構體初始化時完成。若是kobject 未被清空,那麼只須要調用memset() 便可:

memset(kobj, 0, sizeof (*kobj ) );

這多步操做也能夠自kobject.te()來自動處理,它返回一個新分配的kobject:

struct kobject •kobject_create(void);

17.3.6 引用計數

遞增和遞減引用計數:增長一個引用計數可經過koject_getO 函數完成

struct kobject • kobject_get(struct kobject •kobj) ;

該函數正常狀況下將返回一個指向kobject的指針,若是失敗則返回NULL指針:鹼少引用計數經過kobject_putO 完成,這個指令也聲明在《linux/kobject.h》中

void kobject_put(struct kobject •kobj);

kref:該函數將使得引用計數減1 ,若是計數減小到零,則要調用做爲參數提供的release() 函數.注意ON()聲明,提供的時回撥。函數不能簡單地採用兇叫,它必須是一個僅接收一個kref結構體做爲參數的特有函數,並且尚未返回值. kref_put() 函數返回0,但有一種狀況下它返回I,那就是在對該對象的最後一個引用減1時。

17.4 sysfs

sys俗文件系統是一個處於內存中的虛擬文件系統,它爲咱們提供kobject 對象層次結構的視圈。幫助用戶能以一個簡單文件系統的方式來觀察系統中各類設備的拓撲結構.藉助屬性對象, kobject 能夠用導出文件的方式,將內核變量提供給用戶讀取或寫入sysfs 的訣竅是把kobject 對象與目錄項(directory entries)緊密聯繫起來,這點是經過kobject 對象中的dentry 字段實現的.

因爲kobject 被映射到目錄項,同時對象層次結構也已經在內存中造成了一棵樹,所以sys的生成便水到渠成般地簡單了.

Sysf單的根目錄下包含了至少十個目錄。其中最重要的目錄是devices , 該目錄將設備模型導出到用戶空間. 目錄結構就是系統中實際的設備拓撲

17.4.1 sysfs 中添加和刪除kobject

僅僅初始化kobject 是不能自動將其導出到sysfs 中的,想要把kot加ct 導入sysfs,你須要用到函數kobject_add():

int kobject_add (struct kobj ect •kobj , struct kobj ect •parent, const char •fmt , . .. ) ;

輔助函數kobject_create_and_add()把kobject_ createO 和koect_add一個函數中:

struct kobject •kobject_create_and_add(const char • name, struct kobject •parent) ;

從sysfs 中刪除一個kobject 對應文件目錄,需使用函數kobject_del() :

void kobject _del(struct kobject *kobj );

17.4.2 向sysfs 中添加文件

默認屬性:默認的文件集合是經過kobject 和kset 中的ktype 字段提供的。所以全部具備相同類型的kobject 在它們對應的sysfs 目錄下都擁有相同的默認文件集合。其中名稱字段提供了該屬性的名稱,最終出如今sysfs 中的文件名就是它。owner 字段在存在所屬模塊的狀況下指向其所屬的module結構體。store()方法在寫操做時調用,它會從buffer 中讀取size 大小的字節,並將其存放入a即表示的屬性結構體變量中。緩衝區的大小老是爲PAGE_SIZE 或更小些。該函數若是執行成功,則將返回實際從buffer 中讀取的字節數:若是失敗,則返回負數的錯誤碼。

建立新屬性:事實上,由於全部具備相同ktype 的kobject,在本質上區別不大的狀況下,都應是相互接近的.也就是說,好比對於全部的分區而言,它們徹底能夠具備一樣的屬性集會。這不但可讓事情簡單,有助於代碼合井,還使相似對象在sy的目錄中外現一致。sysfs_create_ file()接口:

int sysfs_create_file(struct kobject *kobj, const struct attribute *attr);

除了添加文件外,還有可能須要建立符號鏈接。在sysfs 中建立一個符號鏈接至關簡單:

int sysfs_create_link(struct kobject *kobj, struct kobject ·get, char name);

刪除新屬性:刪除一個屬性需經過函數sysfs_remove_ file() 完成:

void sysfs_remove_file (struct kobject *kobj, const struct attribute *at tr);

一旦調用返回,給定的屬性將再也不存在於給定的ko均ect 目錄中。另外由sysfs_ creat_ link()建立的符號鏈接可經過函數sysfs_remove link()刪除:

void sysfs_remove_link(struct kobject *kobj , char *name);

sysfs約定:當前sy拙文件系統代替了之前須要由ioctl() (做用於設備節點)和procfs 文件系統完成的功能。可是爲了保持sysfs 乾淨和直觀,開發者必須聽從如下約定。

  • 首先, sysfs 屬性應該保證每一個文件只導出一個值,該值應該是文本形式並且映射爲簡單C類型。
  • 其次,在sy峙中要以一個清晰的層次組織數據。
  • 最後,記住sysfs 提供內核到用戶空鬧的服務,這多少有些用戶空間的ABI (應用程序二進制接口〉的做用。

17.4.3 內核事件層

每一個事件都被賦予了一個動詞或動做字符串表示信號。該字符串會以「被修改過」或「未掛載」等詞語來描述事件。在內核代碼中向用戶空間發送信號使用函數kobject_uevent():

int kobject_uevent(struct kobject *kobj,enum kobject_action action);

第一個參數指定發送該信號的koject 對象。實際的內核事件將包含該koject 映射到sysfs 的路徑。第二個參數指定了描述該信號的「動做」或「動詞」

使用kobject 和屬性不但有利於很好的實現基於sysfs 的事件,同時也有利於建立新kojects對象和屬性來表示新對象和數據一一它們還沒有出如今sysfs中.

17.5 小結

  本章中,咱們考察的內核功能涉及設備驅動的實現和設備樹的管理,包括模塊、 kobject (以 及相關的 kset和 ktype)和 sysfs。這些功能對於設備驅動程序的開發者來講是相當重要的,由於 這可以讓他們寫出更爲模塊化、更爲高級的驅動程序. 這章討論了內核中咱們要學習的最後一個子系統,從下面開始要介紹一些廣泛的但卻重要的 主題,這些主題是任何一個內核開發者都須要瞭解的,首先要講的就是調試!

相關文章
相關標籤/搜索