版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)
文檔簡介
1、第第9章章 Linux驅(qū)動程序設(shè)計驅(qū)動程序設(shè)計9.1Linux 設(shè)備驅(qū)動程序設(shè)備驅(qū)動程序9.2Linux經(jīng)典經(jīng)典Hello world驅(qū)動程序驅(qū)動程序9.3Linux字符設(shè)備驅(qū)動程序?qū)嵗址O(shè)備驅(qū)動程序?qū)嵗齃inuxLinux操作系統(tǒng)的內(nèi)核是單一體系結(jié)構(gòu)(操作系統(tǒng)的內(nèi)核是單一體系結(jié)構(gòu)(monolithic kernelmonolithic kernel) 有了模塊機(jī)制后,有了模塊機(jī)制后,提高提高LinuxLinux操作系統(tǒng)的可擴(kuò)充性操作系統(tǒng)的可擴(kuò)充性,內(nèi)核編程不再是一個,內(nèi)核編程不再是一個惡夢惡夢什么是模塊呢?什么是模塊呢? 模塊的全稱是模塊的全稱是“動態(tài)可加載內(nèi)核模塊動態(tài)可加載內(nèi)核模塊”(L
2、oadable Kernel ModuleLoadable Kernel Module,LKMLKM) 模塊在內(nèi)核空間運行模塊在內(nèi)核空間運行 模塊實際上是一種目標(biāo)對象文件模塊實際上是一種目標(biāo)對象文件 沒有鏈接,不能獨立運行沒有鏈接,不能獨立運行,但是其代碼可以在運行時鏈接到系統(tǒng)中,但是其代碼可以在運行時鏈接到系統(tǒng)中作為內(nèi)核的一部分運行或從內(nèi)核中取下,從而可以作為內(nèi)核的一部分運行或從內(nèi)核中取下,從而可以動態(tài)擴(kuò)充內(nèi)核的動態(tài)擴(kuò)充內(nèi)核的功能功能 這種目標(biāo)代碼通常由一組函數(shù)和數(shù)據(jù)結(jié)構(gòu)組成這種目標(biāo)代碼通常由一組函數(shù)和數(shù)據(jù)結(jié)構(gòu)組成優(yōu)點優(yōu)點使得內(nèi)核更加使得內(nèi)核更加緊湊和靈活緊湊和靈活修改內(nèi)核時,修改內(nèi)核時,不
3、必全部重新編譯不必全部重新編譯整個內(nèi)核。系統(tǒng)如果需要使整個內(nèi)核。系統(tǒng)如果需要使用新模塊,只要編譯相應(yīng)的模塊,然后使用用新模塊,只要編譯相應(yīng)的模塊,然后使用insmodinsmod將模塊將模塊裝載即可裝載即可模塊的目標(biāo)代碼一旦被鏈接到內(nèi)核,它的作用域和靜態(tài)鏈接模塊的目標(biāo)代碼一旦被鏈接到內(nèi)核,它的作用域和靜態(tài)鏈接的內(nèi)核目標(biāo)代碼完全等價的內(nèi)核目標(biāo)代碼完全等價缺點缺點由于內(nèi)核所占用的內(nèi)存是不會被換出的,所以鏈接進(jìn)內(nèi)核的由于內(nèi)核所占用的內(nèi)存是不會被換出的,所以鏈接進(jìn)內(nèi)核的模塊會給整個系統(tǒng)帶來一定的模塊會給整個系統(tǒng)帶來一定的性能和內(nèi)存利用方面的損失性能和內(nèi)存利用方面的損失; ;裝入內(nèi)核的模塊就成為內(nèi)核的一
4、部分,可以修改內(nèi)核中的其裝入內(nèi)核的模塊就成為內(nèi)核的一部分,可以修改內(nèi)核中的其他部分,因此,模塊的使用不當(dāng)會導(dǎo)致系統(tǒng)崩潰他部分,因此,模塊的使用不當(dāng)會導(dǎo)致系統(tǒng)崩潰; ;為了讓內(nèi)核模塊能訪問所有內(nèi)核資源,為了讓內(nèi)核模塊能訪問所有內(nèi)核資源,內(nèi)核必須維護(hù)符號表內(nèi)核必須維護(hù)符號表,并在裝入和卸載模塊時修改符號表,并在裝入和卸載模塊時修改符號表; ;模塊會要求利用其它模塊的功能,所以,模塊會要求利用其它模塊的功能,所以,內(nèi)核要維護(hù)模塊之內(nèi)核要維護(hù)模塊之間的依賴性間的依賴性. . C C語言程序語言程序 LinuxLinux內(nèi)核模塊內(nèi)核模塊運行運行 用戶空間用戶空間 內(nèi)核空間內(nèi)核空間入口入口 main()
5、main() module_init()module_init()指定指定; ;出口出口 無無 module_exit()module_exit()指定指定; ; 編譯編譯 gcc gcc c Makefilec Makefile連接連接 ld insmodld insmod運行運行 直接運行直接運行 insmodinsmod調(diào)試調(diào)試 gdb kdbug, kdb, kgdbgdb kdbug, kdb, kgdb等等 Linux系統(tǒng)的設(shè)備分為字符設(shè)備(系統(tǒng)的設(shè)備分為字符設(shè)備(char device)、塊設(shè)備(、塊設(shè)備(block device)和網(wǎng)絡(luò)設(shè)備()和網(wǎng)絡(luò)設(shè)備(network dev
6、ice)三種。)三種。 字符設(shè)備字符設(shè)備是指存取時沒有緩存的設(shè)備。典型的字是指存取時沒有緩存的設(shè)備。典型的字符設(shè)備包括鼠標(biāo),鍵盤,串行口等。符設(shè)備包括鼠標(biāo),鍵盤,串行口等。 字符設(shè)備在字符設(shè)備在I/O傳輸過程中以字符為單位的,但是傳輸過程中以字符為單位的,但是不一定是以字節(jié)為單位,因為一個字符展不一定是以字節(jié)為單位,因為一個字符展16bit(2個字個字節(jié))。它是通過文件系統(tǒng)節(jié)點來存儲,在節(jié))。它是通過文件系統(tǒng)節(jié)點來存儲,在Linux系統(tǒng)中系統(tǒng)中,字符設(shè)備以特別的文件方式在文件目錄樹中占據(jù)位置,字符設(shè)備以特別的文件方式在文件目錄樹中占據(jù)位置并擁有自己的結(jié)點,并且指明了文件類型,但是,操作并擁有自
7、己的結(jié)點,并且指明了文件類型,但是,操作(包括打開、關(guān)閉、讀、寫操作)起來卻和普通文件一(包括打開、關(guān)閉、讀、寫操作)起來卻和普通文件一樣。大部分字符設(shè)備僅僅是數(shù)據(jù)通道,只能順序存取。樣。大部分字符設(shè)備僅僅是數(shù)據(jù)通道,只能順序存取。 當(dāng)字符設(shè)備與主機(jī)連接后,可以使用當(dāng)字符設(shè)備與主機(jī)連接后,可以使用mknod命命令來為設(shè)備創(chuàng)建字符特別文件。例如,為名為令來為設(shè)備創(chuàng)建字符特別文件。例如,為名為/dev/kui的主機(jī)創(chuàng)建字符特別文件的命令如下:的主機(jī)創(chuàng)建字符特別文件的命令如下: mknod /dev/kui c 4 9 其中:其中:4是設(shè)主設(shè)備號;是設(shè)主設(shè)備號;9是次設(shè)備為是次設(shè)備為9;c是字符型是
8、字符型類型標(biāo)記。類型標(biāo)記。 字符設(shè)備和主機(jī)的通信字符設(shè)備和主機(jī)的通信一般采用中斷方式,當(dāng)字一般采用中斷方式,當(dāng)字符設(shè)備上的數(shù)據(jù)傳輸完成時,就通過總線向系統(tǒng)發(fā)出符設(shè)備上的數(shù)據(jù)傳輸完成時,就通過總線向系統(tǒng)發(fā)出中斷處理信號,數(shù)據(jù)傳輸?shù)牡讓涌刂朴芍袛嗵幚沓绦蛑袛嗵幚硇盘?,?shù)據(jù)傳輸?shù)牡讓涌刂朴芍袛嗵幚沓绦蚝驮O(shè)備驅(qū)動程序協(xié)同完成。和設(shè)備驅(qū)動程序協(xié)同完成。 塊設(shè)備塊設(shè)備主要包括硬盤軟盤設(shè)備、主要包括硬盤軟盤設(shè)備、CD-ROM等。塊設(shè)等。塊設(shè)備的讀寫都有緩存來支持,并且塊設(shè)備必須能夠隨機(jī)備的讀寫都有緩存來支持,并且塊設(shè)備必須能夠隨機(jī)存?。ù嫒。╮andom access),字符設(shè)備則沒有這個要求。),字符設(shè)備
9、則沒有這個要求。 塊設(shè)備和字符設(shè)備塊設(shè)備和字符設(shè)備也有相似之處,塊設(shè)備通過位也有相似之處,塊設(shè)備通過位于于 /dev 目錄的文件系統(tǒng)結(jié)點來存取,其讀寫操作跟字目錄的文件系統(tǒng)結(jié)點來存取,其讀寫操作跟字符設(shè)備一樣,可以一次傳送任意數(shù)目的字節(jié)。符設(shè)備一樣,可以一次傳送任意數(shù)目的字節(jié)。1)內(nèi)核在內(nèi)部管理數(shù)據(jù)的方式不同,)內(nèi)核在內(nèi)部管理數(shù)據(jù)的方式不同,塊設(shè)備將信息存儲在固定大塊設(shè)備將信息存儲在固定大小的塊中,每個塊都有自己的地址,數(shù)據(jù)塊的大小通常在小的塊中,每個塊都有自己的地址,數(shù)據(jù)塊的大小通常在51232768字節(jié)之間,并且每個塊之間都能獨立進(jìn)行讀寫操作。字節(jié)之間,并且每個塊之間都能獨立進(jìn)行讀寫操作。
10、2)內(nèi)核)內(nèi)核/驅(qū)動的軟件接口完全不同。驅(qū)動的軟件接口完全不同。在大多數(shù)的在大多數(shù)的UNIX操作系統(tǒng)中操作系統(tǒng)中,塊設(shè)備只支持以塊為單位的訪問方式。,塊設(shè)備只支持以塊為單位的訪問方式。Linux支持以字符方式支持以字符方式來訪問塊設(shè)備,在來訪問塊設(shè)備,在/dev目錄中的塊設(shè)備,均以字符設(shè)備的外觀目錄中的塊設(shè)備,均以字符設(shè)備的外觀出現(xiàn)。所以,字符設(shè)備和塊設(shè)備的區(qū)別主要體現(xiàn)在內(nèi)核中的管出現(xiàn)。所以,字符設(shè)備和塊設(shè)備的區(qū)別主要體現(xiàn)在內(nèi)核中的管理方式,操作方式和內(nèi)核理方式,操作方式和內(nèi)核/設(shè)備驅(qū)動接口上。設(shè)備驅(qū)動接口上。 網(wǎng)絡(luò)設(shè)備網(wǎng)絡(luò)設(shè)備是一個連接在多個主機(jī)之間,并能夠相是一個連接在多個主機(jī)之間,并能夠
11、相互交換數(shù)據(jù)的設(shè)備。它不一定是硬件設(shè)備,也可能是互交換數(shù)據(jù)的設(shè)備。它不一定是硬件設(shè)備,也可能是一個純粹的軟件設(shè)備。一個純粹的軟件設(shè)備。 網(wǎng)絡(luò)設(shè)備網(wǎng)絡(luò)設(shè)備在在Linux里做專門的處理。里做專門的處理。Linux的網(wǎng)絡(luò)的網(wǎng)絡(luò)系統(tǒng)主要是基于系統(tǒng)主要是基于BSD unix的的socket機(jī)制。在系統(tǒng)和驅(qū)機(jī)制。在系統(tǒng)和驅(qū)動程序之間定義有專門的數(shù)據(jù)結(jié)構(gòu)(動程序之間定義有專門的數(shù)據(jù)結(jié)構(gòu)(sk_buff)進(jìn)行數(shù))進(jìn)行數(shù)據(jù)的傳遞。系統(tǒng)里支持對發(fā)送數(shù)據(jù)和接收數(shù)據(jù)的緩存據(jù)的傳遞。系統(tǒng)里支持對發(fā)送數(shù)據(jù)和接收數(shù)據(jù)的緩存,提供流量控制機(jī)制,提供對多協(xié)議的支持。,提供流量控制機(jī)制,提供對多協(xié)議的支持。 網(wǎng)絡(luò)設(shè)備驅(qū)動程序網(wǎng)絡(luò)
12、設(shè)備驅(qū)動程序異步地接收來自外部世界的異步地接收來自外部世界的網(wǎng)絡(luò)數(shù)據(jù)包,通過網(wǎng)絡(luò)數(shù)據(jù)包,通過push操作將進(jìn)來的數(shù)據(jù)包壓向內(nèi)核操作將進(jìn)來的數(shù)據(jù)包壓向內(nèi)核,而塊設(shè)備驅(qū)動程序則將一片數(shù)據(jù)緩沖發(fā)送給內(nèi)核,而塊設(shè)備驅(qū)動程序則將一片數(shù)據(jù)緩沖發(fā)送給內(nèi)核,驅(qū)動程序?qū)⑾鄳?yīng)設(shè)備的特征信息登記到內(nèi)核中特定的驅(qū)動程序?qū)⑾鄳?yīng)設(shè)備的特征信息登記到內(nèi)核中特定的數(shù)據(jù)結(jié)構(gòu)中。數(shù)據(jù)結(jié)構(gòu)中。 塊設(shè)備驅(qū)動程序可以使用文件形式來描述,而網(wǎng)塊設(shè)備驅(qū)動程序可以使用文件形式來描述,而網(wǎng)絡(luò)驅(qū)動程序則不可以使用一般的文件讀寫操作調(diào)用,絡(luò)驅(qū)動程序則不可以使用一般的文件讀寫操作調(diào)用,網(wǎng)絡(luò)設(shè)備驅(qū)動程序有自己的內(nèi)存名字空間,使用網(wǎng)絡(luò)設(shè)備驅(qū)動程序有自己
13、的內(nèi)存名字空間,使用push等操作來完成數(shù)據(jù)包的轉(zhuǎn)換與遞送。等操作來完成數(shù)據(jù)包的轉(zhuǎn)換與遞送。 內(nèi)核中有專門為網(wǎng)絡(luò)設(shè)備驅(qū)動程序設(shè)計的數(shù)據(jù)包操作接口,內(nèi)核中有專門為網(wǎng)絡(luò)設(shè)備驅(qū)動程序設(shè)計的數(shù)據(jù)包操作接口,如圖如圖9-1所示。路由就是通過網(wǎng)絡(luò)接口所示。路由就是通過網(wǎng)絡(luò)接口sn0到達(dá)網(wǎng)絡(luò)到達(dá)網(wǎng)絡(luò)snullnet0的意的意思,使用思,使用route add -net snullnet0 dev sn0指令來完成,指令來完成,Linux 2.2版本以上內(nèi)核就不用這樣做,內(nèi)核將自動添加。版本以上內(nèi)核就不用這樣做,內(nèi)核將自動添加。遠(yuǎn)程 Linux中大部分的驅(qū)動程序都中大部分的驅(qū)動程序都以模塊的形式編寫以模塊的形
14、式編寫,這些驅(qū)動程序源碼可以修改到內(nèi)核中,也可以把他們這些驅(qū)動程序源碼可以修改到內(nèi)核中,也可以把他們編譯成模塊形式,在需要的時候動態(tài)加載。例如,在編譯成模塊形式,在需要的時候動態(tài)加載。例如,在塊驅(qū)動設(shè)備中,當(dāng)系統(tǒng)試圖讀取這個設(shè)備(即調(diào)用塊驅(qū)動設(shè)備中,當(dāng)系統(tǒng)試圖讀取這個設(shè)備(即調(diào)用read()時),就會運行驅(qū)動程序中的時),就會運行驅(qū)動程序中的block_read() 這個這個函數(shù)。函數(shù)。(1)查看模塊信息命令。)查看模塊信息命令??梢杂每梢杂胠smod、cat /proc/modules、modinfo三個命令查看模塊信息三個命令查看模塊信息(2)modprobe命令的功能是自動處理可載入模塊
15、。命令的功能是自動處理可載入模塊。語法語法:modprobe -acdlrtvV-help模塊文件模塊文件符號名稱符號名稱 = 符號值符號值 (3)insmod(install module)掛載模塊)掛載模塊 。功能說明:載入模塊。功能說明:載入模塊。語法:語法:insmod -fkmpsvxX-o 模塊文件模塊文件符符號名稱號名稱 = 符號值符號值(4)rmmod 移除已掛載模塊。移除已掛載模塊。(5)depmod 創(chuàng)建模塊依賴關(guān)系的列表。創(chuàng)建模塊依賴關(guān)系的列表。這個模塊管理這個模塊管理工具是創(chuàng)建模塊依賴關(guān)系的列表,有幾個參數(shù)需要注工具是創(chuàng)建模塊依賴關(guān)系的列表,有幾個參數(shù)需要注意一下,目前
16、的意一下,目前的Linux 2.6x版本,是自動解決依賴關(guān)系版本,是自動解決依賴關(guān)系 Linux設(shè)備驅(qū)動程序及用戶程序、操作系統(tǒng)內(nèi)容之間設(shè)備驅(qū)動程序及用戶程序、操作系統(tǒng)內(nèi)容之間的關(guān)系及結(jié)構(gòu)的關(guān)系及結(jié)構(gòu)如圖如圖9-2所示。其中所示。其中Linux的設(shè)備驅(qū)動程的設(shè)備驅(qū)動程序大致可以分為:驅(qū)動程序注冊、設(shè)備打開、設(shè)備讀序大致可以分為:驅(qū)動程序注冊、設(shè)備打開、設(shè)備讀寫操作、設(shè)備控制操作、設(shè)備中斷和輪詢處理、設(shè)備寫操作、設(shè)備控制操作、設(shè)備中斷和輪詢處理、設(shè)備釋放、驅(qū)動程序注銷等七個部分。釋放、驅(qū)動程序注銷等七個部分。 向系統(tǒng)增加一個驅(qū)動程序意味著要賦予它一個主向系統(tǒng)增加一個驅(qū)動程序意味著要賦予它一個主設(shè)
17、備號,這可以通過在驅(qū)動程序的初始化過程中調(diào)用設(shè)備號,這可以通過在驅(qū)動程序的初始化過程中調(diào)用register_chrdev()或者或者register_blkdev()來完成。來完成。 打開設(shè)備是通過調(diào)用打開設(shè)備是通過調(diào)用file_operations結(jié)構(gòu)中的函結(jié)構(gòu)中的函數(shù)數(shù)open()來完成的,它是驅(qū)動程序用來為今后的操作完來完成的,它是驅(qū)動程序用來為今后的操作完成初始化準(zhǔn)備工作的。在大部分驅(qū)動程序中,成初始化準(zhǔn)備工作的。在大部分驅(qū)動程序中,open()通通常需要完成下列工作:常需要完成下列工作:檢查設(shè)備相關(guān)錯誤,如設(shè)備未準(zhǔn)備就緒等。如果是第一次打開,則初始化硬件設(shè)備。識別次設(shè)備號,如果有必要
18、則更新讀寫操作的當(dāng)前位置指針f_ops。分配和填寫要放在file-private_data里的數(shù)據(jù)結(jié)構(gòu)。使用計數(shù)器增加1。 但如果是塊設(shè)備,則需要調(diào)用函數(shù)但如果是塊設(shè)備,則需要調(diào)用函數(shù)block_read()和和block_write()來進(jìn)行數(shù)據(jù)讀寫,這兩個函數(shù)將向來進(jìn)行數(shù)據(jù)讀寫,這兩個函數(shù)將向設(shè)備請求表中增加讀寫請求,以便設(shè)備請求表中增加讀寫請求,以便Linux內(nèi)核可以對請求順序進(jìn)內(nèi)核可以對請求順序進(jìn)行優(yōu)化處理,由于內(nèi)核操作是對內(nèi)存緩沖區(qū)而不是直接對設(shè)備行優(yōu)化處理,由于內(nèi)核操作是對內(nèi)存緩沖區(qū)而不是直接對設(shè)備本身,因此能很大程度上加快讀寫速度。如果內(nèi)存緩沖區(qū)中沒本身,因此能很大程度上加快讀寫
19、速度。如果內(nèi)存緩沖區(qū)中沒有所要讀入的數(shù)據(jù),或者需要執(zhí)行寫操作將數(shù)據(jù)寫入設(shè)備,那有所要讀入的數(shù)據(jù),或者需要執(zhí)行寫操作將數(shù)據(jù)寫入設(shè)備,那么就要執(zhí)行真正的數(shù)據(jù)傳輸,這是通過調(diào)用數(shù)據(jù)結(jié)構(gòu)么就要執(zhí)行真正的數(shù)據(jù)傳輸,這是通過調(diào)用數(shù)據(jù)結(jié)構(gòu)blk_dev_struct中的函數(shù)中的函數(shù)request_fn()來完成的。來完成的。 除了讀寫操作外,應(yīng)用程序有時還需要對設(shè)備進(jìn)除了讀寫操作外,應(yīng)用程序有時還需要對設(shè)備進(jìn)行控制,這時可以通過設(shè)備驅(qū)動程序中的行控制,這時可以通過設(shè)備驅(qū)動程序中的函數(shù)函數(shù)ioctl()來來完成,完成,ioctl()的用法與具體設(shè)備密切關(guān)聯(lián),因此需要根的用法與具體設(shè)備密切關(guān)聯(lián),因此需要根據(jù)設(shè)備
20、的實際情況進(jìn)行具體分析。據(jù)設(shè)備的實際情況進(jìn)行具體分析。 對于不支持中斷的硬件設(shè)備,對于不支持中斷的硬件設(shè)備,讀寫時需要輪流查詢讀寫時需要輪流查詢設(shè)備狀態(tài)設(shè)備狀態(tài),以便決定是否繼續(xù)進(jìn)行數(shù)據(jù)傳輸。如果設(shè),以便決定是否繼續(xù)進(jìn)行數(shù)據(jù)傳輸。如果設(shè)備支持中斷,則可以按中斷方式進(jìn)行操作。備支持中斷,則可以按中斷方式進(jìn)行操作。 通過通過Linux提供的系統(tǒng)調(diào)用函數(shù)(例如提供的系統(tǒng)調(diào)用函數(shù)(例如init_module等等)進(jìn)入內(nèi)核,這些函數(shù)在進(jìn)入內(nèi)核,這些函數(shù)在2.6內(nèi)核版本下總共有兩百多內(nèi)核版本下總共有兩百多個,提供了幾乎所有應(yīng)用程序進(jìn)入內(nèi)核所需要執(zhí)行的個,提供了幾乎所有應(yīng)用程序進(jìn)入內(nèi)核所需要執(zhí)行的操作。操作
21、。 系統(tǒng)的內(nèi)核函數(shù)都有系統(tǒng)的內(nèi)核函數(shù)都有“sys_”前綴(例如函數(shù)前綴(例如函數(shù)sys_init_module()),),應(yīng)用程序通過訪問設(shè)備文件系統(tǒng)應(yīng)用程序通過訪問設(shè)備文件系統(tǒng)來調(diào)用這些函數(shù)。這一層主要是來調(diào)用這些函數(shù)。這一層主要是“devfs”(device filesystem())文件管理機(jī)制,它是從普通文件和設(shè)備)文件管理機(jī)制,它是從普通文件和設(shè)備文件抽象出來的一個文件系統(tǒng)層,完成進(jìn)入具體的設(shè)文件抽象出來的一個文件系統(tǒng)層,完成進(jìn)入具體的設(shè)備文件操作之前的準(zhǔn)備工作。備文件操作之前的準(zhǔn)備工作。 釋放設(shè)備是通過調(diào)用釋放設(shè)備是通過調(diào)用file_operations結(jié)構(gòu)中的函結(jié)構(gòu)中的函數(shù)數(shù)re
22、lease()來完成的,這個設(shè)備方法有時也被稱為來完成的,這個設(shè)備方法有時也被稱為close(),它的作用正好與,它的作用正好與open()相反,通常要完成下列相反,通常要完成下列工作:工作: 使用計數(shù)器減1。釋放在file-private_data中分配的內(nèi)存。如果計數(shù)器為0,則關(guān)閉設(shè)備。 向系統(tǒng)增加一個驅(qū)動程序意味著要賦予它一個主向系統(tǒng)增加一個驅(qū)動程序意味著要賦予它一個主設(shè)備號,設(shè)備號,這可以通過在驅(qū)動程序的初始化過程中調(diào)用這可以通過在驅(qū)動程序的初始化過程中調(diào)用register_chrdev()或者或者register_blkdev()來完成。而在關(guān)來完成。而在關(guān)閉字符設(shè)備或者塊設(shè)備時,則
23、需要通過調(diào)用閉字符設(shè)備或者塊設(shè)備時,則需要通過調(diào)用unregister_chrdev()或或unregister_blkdev()從內(nèi)核中注從內(nèi)核中注銷設(shè)備,同時釋放占用的主設(shè)備號。銷設(shè)備,同時釋放占用的主設(shè)備號。(1)file結(jié)構(gòu)。結(jié)構(gòu)。file結(jié)構(gòu)是設(shè)備驅(qū)動程序一個重要的數(shù)據(jù)結(jié)構(gòu),指結(jié)構(gòu)是設(shè)備驅(qū)動程序一個重要的數(shù)據(jù)結(jié)構(gòu),指示當(dāng)前系統(tǒng)中已打開的文件。它在示當(dāng)前系統(tǒng)中已打開的文件。它在C語言庫中定義,在調(diào)用內(nèi)語言庫中定義,在調(diào)用內(nèi)核核open函數(shù)時創(chuàng)建,并傳遞給在該設(shè)備上進(jìn)行操作的所有函數(shù)函數(shù)時創(chuàng)建,并傳遞給在該設(shè)備上進(jìn)行操作的所有函數(shù),直到最后的,直到最后的close()函數(shù)執(zhí)行完畢。函數(shù)執(zhí)
24、行完畢。file結(jié)構(gòu)中還包含了指向它結(jié)構(gòu)中還包含了指向它所擁有的所擁有的file_operations結(jié)構(gòu)的指針。結(jié)構(gòu)的指針。 file代表一個打開的文件,在執(zhí)行代表一個打開的文件,在執(zhí)行file_operations中的中的open操操作時被創(chuàng)建,這里需要注意的是與用戶空間作時被創(chuàng)建,這里需要注意的是與用戶空間inode指針的區(qū)別,指針的區(qū)別,一個在內(nèi)核;而一個在內(nèi)核;而file指針在用戶空間,由指針在用戶空間,由C庫來定義。庫來定義。 inode結(jié)構(gòu)由內(nèi)核自動生成,代表已打開文件的描述符,與每結(jié)構(gòu)由內(nèi)核自動生成,代表已打開文件的描述符,與每個打開的文件一一對應(yīng)。個打開的文件一一對應(yīng)。ino
25、de被內(nèi)核用來代表一個文件,注意被內(nèi)核用來代表一個文件,注意和和struct file的區(qū)別在于:的區(qū)別在于:struct inode一個是代表文件,而一個是代表文件,而struct file一個是代表打開的文件。一個是代表打開的文件。 dev_t i_rdev 設(shè)備文件的設(shè)備號設(shè)備文件的設(shè)備號 struct cdev *i_cdev代表字符設(shè)備的數(shù)據(jù)結(jié)構(gòu)代表字符設(shè)備的數(shù)據(jù)結(jié)構(gòu) dev_t擴(kuò)展到擴(kuò)展到32位,其中位,其中12位主設(shè)備號,位主設(shè)備號,20位從設(shè)備號;而位從設(shè)備號;而cdev用于存儲一個指向字符設(shè)備文件的指針。用于存儲一個指向字符設(shè)備文件的指針。 inode結(jié)構(gòu)是用來在內(nèi)核內(nèi)部表示
26、文件的,同一個文件可以結(jié)構(gòu)是用來在內(nèi)核內(nèi)部表示文件的,同一個文件可以被多次打開,所以可以對應(yīng)很多被多次打開,所以可以對應(yīng)很多file結(jié)構(gòu),但是只對應(yīng)一個結(jié)構(gòu),但是只對應(yīng)一個inode結(jié)構(gòu)。結(jié)構(gòu)。 在在Linux內(nèi)核下使用內(nèi)核下使用file_operations數(shù)據(jù)結(jié)構(gòu),來數(shù)據(jù)結(jié)構(gòu),來建立設(shè)備驅(qū)動程序中的函數(shù)與主設(shè)備號(建立設(shè)備驅(qū)動程序中的函數(shù)與主設(shè)備號(major number)之間的對應(yīng)關(guān)系。該數(shù)據(jù)結(jié)構(gòu)中包含了指向)之間的對應(yīng)關(guān)系。該數(shù)據(jù)結(jié)構(gòu)中包含了指向驅(qū)動程序內(nèi)部大多數(shù)函數(shù)的指針,描述了虛擬文件系驅(qū)動程序內(nèi)部大多數(shù)函數(shù)的指針,描述了虛擬文件系統(tǒng)如何操作一個打開的外圍設(shè)備。統(tǒng)如何操作一個打開
27、的外圍設(shè)備。file_operations是把是把系統(tǒng)調(diào)用和驅(qū)動程序關(guān)聯(lián)起來的關(guān)鍵數(shù)據(jù)結(jié)構(gòu),這個系統(tǒng)調(diào)用和驅(qū)動程序關(guān)聯(lián)起來的關(guān)鍵數(shù)據(jù)結(jié)構(gòu),這個結(jié)構(gòu)的每一個成員都對應(yīng)著一個系統(tǒng)調(diào)用。結(jié)構(gòu)的每一個成員都對應(yīng)著一個系統(tǒng)調(diào)用。 先讀取先讀取file_operation中相應(yīng)的函數(shù)指針,接著把控中相應(yīng)的函數(shù)指針,接著把控制權(quán)轉(zhuǎn)交給函數(shù),從而完成了制權(quán)轉(zhuǎn)交給函數(shù),從而完成了Linux設(shè)備驅(qū)動程序的工設(shè)備驅(qū)動程序的工作。作。file_operations是一個字符設(shè)備把驅(qū)動的操作和設(shè)是一個字符設(shè)備把驅(qū)動的操作和設(shè)備號聯(lián)系在一起的紐帶,是一系列指針的集合,每個備號聯(lián)系在一起的紐帶,是一系列指針的集合,每個被打
28、開的文件都對應(yīng)于一系列的操作,這就是被打開的文件都對應(yīng)于一系列的操作,這就是file_operations,用來執(zhí)行一系列的系統(tǒng)調(diào)用。,用來執(zhí)行一系列的系統(tǒng)調(diào)用。 在系統(tǒng)內(nèi)部,在系統(tǒng)內(nèi)部,I/O設(shè)備的存取操作通過特定的入口點設(shè)備的存取操作通過特定的入口點來進(jìn)行,而這組特定的入口點是由設(shè)備驅(qū)動程序提供來進(jìn)行,而這組特定的入口點是由設(shè)備驅(qū)動程序提供的。通常這組設(shè)備驅(qū)動程序接口是由結(jié)構(gòu)的。通常這組設(shè)備驅(qū)動程序接口是由結(jié)構(gòu)file_operations結(jié)構(gòu)體向系統(tǒng)說明的,它的定義在結(jié)構(gòu)體向系統(tǒng)說明的,它的定義在include/linux/fs.h中。中。 一個一個file_operation 結(jié)構(gòu)或者
29、其一個指針稱為結(jié)構(gòu)或者其一個指針稱為 fops( 或者它的一些變體)。結(jié)構(gòu)中的每個成員必須指向驅(qū)或者它的一些變體)。結(jié)構(gòu)中的每個成員必須指向驅(qū)動中的函數(shù),這些函數(shù)實現(xiàn)一個特別的操作,或者對動中的函數(shù),這些函數(shù)實現(xiàn)一個特別的操作,或者對于不支持的操作置為于不支持的操作置為 NULL。struct file_operations struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char _user *, size_t, loff_t *); ss
30、ize_t (*write) (struct file *, const char _user *, size_t, loff_t *); ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t); ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t); int (*readdir) (struct file *, void *, filldir_t); unsigned int
31、 (*poll) (struct file *, struct poll_table_struct *); int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); long (*compat_ioctl) (struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct v
32、m_area_struct *); int (*open) (struct inode *, struct file *); int (*flush) (struct file *, fl_owner_t id); int (*release) (struct inode *, struct file *); int (*fsync) (struct file *, struct dentry *, int datasync); int (*aio_fsync) (struct kiocb *, int datasync); int (*fasync) (int, struct file *,
33、 int); int (*lock) (struct file *, int, struct file_lock *); ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long,unsigned long, unsigned long); int (*check_flags)(int); int (*dir_notify)(struct
34、file *filp, unsigned long arg); int (*flock) (struct file *, int, struct file_lock *); ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t,unsigned int); ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t,unsigned int); int (*setlease)(stru
35、ct file *, long, struct file_lock *); int (*fsetattr)(struct file *, struct iattr *); 根據(jù)上文提到過的,舊版本根據(jù)上文提到過的,舊版本Hello World驅(qū)動程序驅(qū)動程序代碼如下:代碼如下:#define MODULE #include #include int init_module(void) printk(KERN_INFO Hello, worldn); return 0; void cleanup_module(void) printk(KERN_INFO Goodbye cruel worldn
36、); 在在Linux2.6版本中的版本中的hello world驅(qū)動與上述驅(qū)動與上述2.4版本有所不同,具版本有所不同,具體內(nèi)容如下:體內(nèi)容如下: #include #include #include MODULE_LICENSE(GPL);/新新, 否則有否則有waring, 去掉了去掉了#define MODULE, 自動定義自動定義 static int hello_init(void) printk(KERN_ALERT Hello, worldn); return 0; static void hello_exit(void) printk(KERN_ALERT Goodbye, c
37、ruel worldn); module_init(hello_init);/這行是必需的這行是必需的module_exit(hello_exit);/這行是必需的這行是必需的 在在Linux2.6版本中,采用版本中,采用module_init與與module_exit兩個宏來定義驅(qū)動程序的注冊與注消,這兩個宏來定義驅(qū)動程序的注冊與注消,這兩個宏在兩個宏在init.h中定義,中定義,module_init與與module_exit宏的宏的代碼如下:代碼如下:/* 每一個模塊使用前必須進(jìn)行初始化每一個模塊使用前必須進(jìn)行初始化 */#define module_init(initfn) stati
38、c inline initcall_t _inittest(void) return initfn; int init_module(void) _attribute_(alias(#initfn); /* 這個只是在想要注銷模塊是調(diào)用這個只是在想要注銷模塊是調(diào)用*/ #define module_exit(exitfn) static inline exitcall_t _exittest(void) return exitfn; void cleanup_module(void) _attribute_(alias(#exitfn);從上述代碼可以看出,從上述代碼可以看出,module_i
39、nit實際上是把用戶程序中的實際上是把用戶程序中的module_init(initfn)一行代碼分解成一行代碼分解成_inittest(void)和和init_module(void)兩個函數(shù)。兩個函數(shù)。實現(xiàn)實現(xiàn)_inittest函數(shù)比較簡單,就是返回函數(shù)比較簡單,就是返回initfn,而,而initfn對應(yīng)的是用對應(yīng)的是用戶戶module_init函數(shù)中的參數(shù),代表用戶自定義的驅(qū)動程序入口函數(shù)中的參數(shù),代表用戶自定義的驅(qū)動程序入口真正地實現(xiàn)函數(shù)地址。真正地實現(xiàn)函數(shù)地址。實現(xiàn)實現(xiàn)init_module函數(shù)復(fù)雜一些,采用了函數(shù)復(fù)雜一些,采用了_attribute_機(jī)制,機(jī)制,_attribute
40、_可以設(shè)置函數(shù)屬性(可以設(shè)置函數(shù)屬性(Function Attribute)、變量)、變量屬性(屬性(Variable Attribute)和類型屬性()和類型屬性(Type Attribute)。)。 上述示例中大量應(yīng)用了上述示例中大量應(yīng)用了printk函數(shù)。函數(shù)。printk函數(shù)允許函數(shù)允許按照相關(guān)的記錄級或優(yōu)先級將消息嚴(yán)格分類和輸出,按照相關(guān)的記錄級或優(yōu)先級將消息嚴(yán)格分類和輸出,并且是內(nèi)核中直接支持的一個消息輸出函數(shù)。并且是內(nèi)核中直接支持的一個消息輸出函數(shù)。 printk函數(shù)通常需要一個宏來指定記錄等級。例如函數(shù)通常需要一個宏來指定記錄等級。例如KERN_INFO、KERN_ALERT等
41、。在前面的例子中看等。在前面的例子中看到過這個宏,它就是消息記錄等級的一種。記錄等級到過這個宏,它就是消息記錄等級的一種。記錄等級宏的作用是擴(kuò)展為一個字符串,這個字符串會在編譯宏的作用是擴(kuò)展為一個字符串,這個字符串會在編譯期間與相應(yīng)的消息文本相連接,這樣在優(yōu)先級和格式期間與相應(yīng)的消息文本相連接,這樣在優(yōu)先級和格式化字符串之間沒有逗號。化字符串之間沒有逗號。 printk()函數(shù)是直接使用了向終端寫函數(shù)函數(shù)是直接使用了向終端寫函數(shù)tty_write()。而。而printf()函數(shù)是調(diào)用函數(shù)是調(diào)用write()系統(tǒng)調(diào)用函數(shù)向標(biāo)準(zhǔn)輸系統(tǒng)調(diào)用函數(shù)向標(biāo)準(zhǔn)輸出設(shè)備寫。所以在用戶態(tài)(如進(jìn)程出設(shè)備寫。所以在用
42、戶態(tài)(如進(jìn)程0)不能夠直接使用)不能夠直接使用printk()函數(shù),而在內(nèi)核態(tài)由于他已是特權(quán)級,所以無函數(shù),而在內(nèi)核態(tài)由于他已是特權(quán)級,所以無需系統(tǒng)調(diào)用來改變特權(quán)級,因而能夠直接使用需系統(tǒng)調(diào)用來改變特權(quán)級,因而能夠直接使用printk()函數(shù)。函數(shù)。1驅(qū)動程序的驅(qū)動程序的make文件文件 模塊的建立過程與用戶應(yīng)用程序的建立過程有顯著模塊的建立過程與用戶應(yīng)用程序的建立過程有顯著不同,內(nèi)核是一個大的、獨立的程序,對于它的各個不同,內(nèi)核是一個大的、獨立的程序,對于它的各個部分如何組合在一起有詳細(xì)而明確的要求。部分如何組合在一起有詳細(xì)而明確的要求。(1)保證內(nèi)核版本足夠新,包括編譯器、模塊工具,以)保
43、證內(nèi)核版本足夠新,包括編譯器、模塊工具,以及其他必要工具等。及其他必要工具等。(2)參考在內(nèi)核文檔目錄下的文件)參考在內(nèi)核文檔目錄下的文件 Documentation/Changes,它列出了需要的工具版本;,它列出了需要的工具版本;因為建立一個內(nèi)核(包括它的模塊),如果用錯誤的因為建立一個內(nèi)核(包括它的模塊),如果用錯誤的工具版本,可能導(dǎo)致很多奇怪的問題。工具版本,可能導(dǎo)致很多奇怪的問題。(3)如果沒有源碼樹在文件系統(tǒng)上,或者還沒有配置和)如果沒有源碼樹在文件系統(tǒng)上,或者還沒有配置和建立內(nèi)核,就無法為建立內(nèi)核,就無法為 Linux2.6 內(nèi)核建立可加載的模塊內(nèi)核建立可加載的模塊。(4)如果已
44、建立起所有東西,給模塊創(chuàng)建一個)如果已建立起所有東西,給模塊創(chuàng)建一個makefile并不是非常復(fù)雜,例如對于本節(jié)的并不是非常復(fù)雜,例如對于本節(jié)的“hello world”程程序,只需要采用單行指令即要完成:序,只需要采用單行指令即要完成: obj-m := hello.o obj-m表示該文件將以模塊的方式編譯,因為本模塊由多個文件組表示該文件將以模塊的方式編譯,因為本模塊由多個文件組成,采用模塊名加成,采用模塊名加objs(minix-objs)后綴的形式來定義模塊)后綴的形式來定義模塊的組成文件。當(dāng)然,在使用的組成文件。當(dāng)然,在使用“obj-m := hello.o”這一行之前,這一行之前
45、,還需要做一些其他的準(zhǔn)備工作,這些準(zhǔn)備工作將要用到如下一還需要做一些其他的準(zhǔn)備工作,這些準(zhǔn)備工作將要用到如下一些定義:些定義:$(KERNELDIR)定義了代碼樹的位置,定義了代碼樹的位置,PWD定義了當(dāng)前文件夾位定義了當(dāng)前文件夾位置。置。make命令中命令中-C選項指定了代碼樹的位置(由選項指定了代碼樹的位置(由KERNELDIR給出)。給出)?!癕=”表示這是個外部模塊,表示這是個外部模塊,M=$(PWD)指定了該模塊文件所在指定了該模塊文件所在的路徑。的路徑。makefile預(yù)定義了預(yù)定義了$(PWD)變量,此處可以不必重復(fù)定變量,此處可以不必重復(fù)定義。義。如果是如果是obj-y,那這部
46、分代碼就會編譯成內(nèi)核的一部分,如果是,那這部分代碼就會編譯成內(nèi)核的一部分,如果是obj-m,那么就會編譯成一個單獨的驅(qū)動模塊。,那么就會編譯成一個單獨的驅(qū)動模塊。(5)上面不是一個傳統(tǒng)的)上面不是一個傳統(tǒng)的makefile,實際上是內(nèi)核建立,實際上是內(nèi)核建立系統(tǒng)處理了余下的工作。系統(tǒng)處理了余下的工作。(6)上面的例子(它利用了由)上面的例子(它利用了由 GNU make 提供的擴(kuò)展提供的擴(kuò)展語法)表明有一個模塊要從目標(biāo)文件語法)表明有一個模塊要從目標(biāo)文件 hello.o 建立,在建立,在從目標(biāo)文件建立后結(jié)果模塊命名為從目標(biāo)文件建立后結(jié)果模塊命名為 hello.ko。反之,如果有一個模塊名為反之
47、,如果有一個模塊名為 module.ko,并且來自,并且來自2個源文件(假個源文件(假設(shè)名為設(shè)名為file1.c 和和 file2.c),正確的書寫應(yīng)當(dāng)是:),正確的書寫應(yīng)當(dāng)是: obj-m := module.o module-objs := file1.o file2.o 對于(對于(4)中的)中的makefile,它必須在更大的內(nèi)核建立系統(tǒng)的上下文,它必須在更大的內(nèi)核建立系統(tǒng)的上下文被調(diào)用,如果內(nèi)核源碼位于被調(diào)用,如果內(nèi)核源碼位于 /kernel-2.6 目錄,用來建立模塊的目錄,用來建立模塊的 make 命令(在包含模塊源碼和命令(在包含模塊源碼和makefile 的目錄下鍵入)會是:
48、的目錄下鍵入)會是:make -C /kernel-2.6 M=pwd modules命令先是改變它的目錄到用命令先是改變它的目錄到用 -C 選項提供的目錄下(內(nèi)核選項提供的目錄下(內(nèi)核源碼目錄),它會在那里發(fā)現(xiàn)內(nèi)核的頂層源碼目錄),它會在那里發(fā)現(xiàn)內(nèi)核的頂層 makefile。M=選項使選項使 makefile在試圖建立模塊目標(biāo)前,回到模塊在試圖建立模塊目標(biāo)前,回到模塊源碼目錄。目標(biāo)依次指在源碼目錄。目標(biāo)依次指在 obj-m 變量中發(fā)現(xiàn)的模塊列變量中發(fā)現(xiàn)的模塊列表,在示例里假設(shè)為表,在示例里假設(shè)為module.o。輸入輸入make命令總是感覺繁瑣,所以內(nèi)核開發(fā)者就開發(fā)了命令總是感覺繁瑣,所以內(nèi)
49、核開發(fā)者就開發(fā)了一種一種makefile方式,方便那些在內(nèi)核樹之外建立模塊的方式,方便那些在內(nèi)核樹之外建立模塊的開發(fā)者,開發(fā)者,makefile如下所示:如下所示:#如果如果KERNELRELEASE被定義被定義, 表明已經(jīng)引用表明已經(jīng)引用 kernel建立內(nèi)核系建立內(nèi)核系統(tǒng)并能使用它的語言統(tǒng)并能使用它的語言ifneq ($(KERNELRELEASE),) obj-m := hello.o # 否則稱為直接命令行否則稱為直接命令行, 引用引用kernel可建立內(nèi)核系統(tǒng)可建立內(nèi)核系統(tǒng)else KERNELDIR ?= /lib/modules/$(shell uname -r)/build P
50、WD := $(shell pwd) default: $(MAKE) -C $(KERNELDIR) M=$(PWD) modulesEndif 這個這個makefile在建立中要被讀在建立中要被讀 2 次,當(dāng)從命令行中調(diào)用這個次,當(dāng)從命令行中調(diào)用這個makefile 時,發(fā)現(xiàn)沒有設(shè)置時,發(fā)現(xiàn)沒有設(shè)置 KERNELRELEASE 變量,它利用變量,它利用已安裝模塊目錄中的內(nèi)核建立樹來定位內(nèi)核源碼目錄。如果實已安裝模塊目錄中的內(nèi)核建立樹來定位內(nèi)核源碼目錄。如果實際上沒有運行建立的內(nèi)核,可以在命令行提供一個際上沒有運行建立的內(nèi)核,可以在命令行提供一個KERNELDIR= 選項,設(shè)置選項,設(shè)置KE
51、RNELDIR環(huán)境變量或者修改環(huán)境變量或者修改 makefile 中設(shè)置中設(shè)置 KERNELDIR 的這一行,一旦發(fā)現(xiàn)內(nèi)核源碼樹的這一行,一旦發(fā)現(xiàn)內(nèi)核源碼樹,makefile調(diào)用調(diào)用default:目標(biāo)來運行第:目標(biāo)來運行第 2 個個 make 命令(在命令(在 makefile 里參數(shù)化成里參數(shù)化成 $(MAKE))像前面描述過的一樣來調(diào)用內(nèi))像前面描述過的一樣來調(diào)用內(nèi)核建立系統(tǒng)。在第核建立系統(tǒng)。在第2次被讀,次被讀,makefile 設(shè)置設(shè)置 obj-m,并且內(nèi)核的,并且內(nèi)核的 makefile 文件完成實際的建立模塊工作。文件完成實際的建立模塊工作。 上面的不是一個完整的上面的不是一個完
52、整的makefile,一個完整的,一個完整的 makefile通常包括通常包括過期目標(biāo)文件的清除、文件的安裝等模塊。過期目標(biāo)文件的清除、文件的安裝等模塊。(1)編譯,在終端中轉(zhuǎn)到)編譯,在終端中轉(zhuǎn)到hello.c和和make文件的目錄下,文件的目錄下,只要輸入只要輸入make命令即可編譯。編譯成功后,生成一個命令即可編譯。編譯成功后,生成一個hello.o文件,同時還生成一個文件,同時還生成一個hello.ko文件。文件。(2)手工加載驅(qū)動模塊,在終端中輸入命令)手工加載驅(qū)動模塊,在終端中輸入命令insmod ./ hello.ko 加載驅(qū)動模塊到內(nèi)核。加載驅(qū)動模塊到內(nèi)核。(3)查看驅(qū)動模塊信
53、息,輸入)查看驅(qū)動模塊信息,輸入cat /proc/modules | grep hello 查看查看hello模塊在內(nèi)核中的地址,不加過濾器可以模塊在內(nèi)核中的地址,不加過濾器可以看到全部加載的模塊??吹饺考虞d的模塊。(4)顯示模塊,輸入)顯示模塊,輸入lsmod 顯示模塊,這時可以看到所顯示模塊,這時可以看到所有的模塊名字,后面跟的是主設(shè)備號和次設(shè)備號。有的模塊名字,后面跟的是主設(shè)備號和次設(shè)備號。(5)卸載模塊,輸入)卸載模塊,輸入rmmod key_test,把模塊從內(nèi)核里,把模塊從內(nèi)核里卸載。卸載。(6)在)在Ubuntu終端中,如果看不到加載和卸載終端中,如果看不到加載和卸載Hell
54、o模模塊的塊的printk函數(shù)輸入的消息,這時可以通過函數(shù)輸入的消息,這時可以通過dmesg命令命令查看查看printk函數(shù)輸出的函數(shù)輸出的“Hello,World!”消息。消息。1頭文件定義頭文件定義 在頭文件中,除了包括驅(qū)動程序所必需的頭文件,在頭文件中,除了包括驅(qū)動程序所必需的頭文件,還定義了還定義了file_operations實例實例char_ops,該實例將在模,該實例將在模塊加載函數(shù)塊加載函數(shù)char_init中使用。在中使用。在char_ops結(jié)構(gòu)中,只結(jié)構(gòu)中,只包括了最常見的包括了最常見的read、write、open和和release屬性屬性 驅(qū)動程序是內(nèi)核的一部分,因此需
55、要給其添加模塊驅(qū)動程序是內(nèi)核的一部分,因此需要給其添加模塊初始化函數(shù),該函數(shù)用來完成對所控設(shè)備的初始化,初始化函數(shù),該函數(shù)用來完成對所控設(shè)備的初始化,并調(diào)用并調(diào)用register_chrdev() 函數(shù)注冊字符設(shè)備。模塊初始函數(shù)注冊字符設(shè)備。模塊初始化函數(shù)名為化函數(shù)名為char_init,通過宏,通過宏module_init(char_init)指指定成別名定成別名init_module的實現(xiàn)函數(shù)。模塊初始化函數(shù)代的實現(xiàn)函數(shù)。模塊初始化函數(shù)代碼如下:碼如下:#include chardev.hstatic int _init char_init(void) dev_t dev; printk(
56、KERN_ALERT字符設(shè)備字符設(shè)備chardev設(shè)備初始化設(shè)備初始化.n); dev=MKDEV(DP_MAJOR,DP_MINOR); chardev = cdev_alloc( ); if(chardev=NULL) return -1; if(register_chrdev_region(dev,10,chardev)0) printk(KERN_ALERT字符設(shè)備字符設(shè)備chardev注冊出錯!注冊出錯!n); return -1; chropen=0; len=0; cdev_init(chardev,&char_ops); if(cdev_add(chardev, dev
57、, 1)private_data中的所有內(nèi)容。中的所有內(nèi)容。在最后一次關(guān)閉操作時關(guān)閉設(shè)備。在最后一次關(guān)閉操作時關(guān)閉設(shè)備。關(guān)閉設(shè)代碼如下:關(guān)閉設(shè)代碼如下:static int char_release(struct inode *inode,struct file *file) chropen-; printk(KERN_ALERT注消字符設(shè)備注消字符設(shè)備chardev!n); module_put(THIS_MODULE); return 0; 驅(qū)動驅(qū)動module的的file_operations結(jié)構(gòu)體中可以定義結(jié)構(gòu)體中可以定義很多設(shè)備操作服務(wù)函數(shù),實現(xiàn)這些函數(shù)與系統(tǒng)調(diào)用,很多設(shè)備操作服務(wù)函
58、數(shù),實現(xiàn)這些函數(shù)與系統(tǒng)調(diào)用,或者外部應(yīng)用程序交互,而不管具體的設(shè)備操作。下或者外部應(yīng)用程序交互,而不管具體的設(shè)備操作。下面以面以read函數(shù)為例介紹驅(qū)動函數(shù)的調(diào)用過程。函數(shù)為例介紹驅(qū)動函數(shù)的調(diào)用過程。(1)read實現(xiàn)過程。實現(xiàn)過程。read函數(shù)的原型如下:函數(shù)的原型如下: ssize_t read(struct file *filp, char _user *buf, size_t count, loff_t *f_pos)(2)write的實現(xiàn)過程。的實現(xiàn)過程。write的函數(shù)原型如下:的函數(shù)原型如下:static int char_write(struct file *filp, con
59、st char _user *buffer, size_t length, loff_t *offset)static int char_read(struct file *filp, char _user *buffer, size_t length, loff_t *offset) if(length12) if(copy_to_user(buffer, hello world!,12) printk(KERN_ALERT字符設(shè)備字符設(shè)備chardev讀操作出錯!讀操作出錯!n); return 0; else if(copy_to_user(buffer, hello world!, l
60、ength) printk(KERN_ALERT字符設(shè)備字符設(shè)備chardev讀操作出錯!讀操作出錯!n); return 0; printk(KERN_ALERT添加字符設(shè)備添加字符設(shè)備chardev讀操作成功!讀操作成功!n); return 1;static int char_write(struct file *filp, const char _user *buffer, size_t length, loff_t *offset) printk(KERN_ALERT添加字符設(shè)備添加字符設(shè)備chardev寫操作寫操作!n); return 0; 卸載驅(qū)動與前面的初始化驅(qū)動程序相反。每個重要的
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 急診護(hù)士的工作體會
- 美容美發(fā)行業(yè)化妝師培訓(xùn)心得
- 玩具行業(yè)助理工作總結(jié)
- 醫(yī)務(wù)室護(hù)士的工作感悟
- 咨詢行業(yè)行政后勤工作總結(jié)
- 服務(wù)員的服務(wù)技巧與服務(wù)態(tài)度
- 生物知識綜合講解計劃
- 完善酒店營銷策略
- 咨詢行業(yè)美工工作總結(jié)
- 稅務(wù)籌劃實踐感悟
- (高清版)TDT 1013-2013 土地整治項目驗收規(guī)程
- 我國農(nóng)村社會保障制度存在的問題分析及對策樣本
- 西晉的短暫統(tǒng)一和北方各族的內(nèi)遷 一等獎
- RTO工藝流程簡介
- 語文新課標(biāo)背景下單元整體教學(xué):六下第4單元大單元設(shè)計
- 最高人民法院民事審判第一庭裁判觀點侵權(quán)責(zé)任卷
- 提高自我意識的方法
- 長租公寓課件
- 《康復(fù)護(hù)理??啤氛n件
- 2024年度醫(yī)院肝膽胰脾外科帶教計劃課件
- 品質(zhì)部規(guī)劃方案
評論
0/150
提交評論