Linux設(shè)備驅(qū)動開發(fā)課件_第1頁
Linux設(shè)備驅(qū)動開發(fā)課件_第2頁
Linux設(shè)備驅(qū)動開發(fā)課件_第3頁
Linux設(shè)備驅(qū)動開發(fā)課件_第4頁
Linux設(shè)備驅(qū)動開發(fā)課件_第5頁
已閱讀5頁,還剩57頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

1、Linux設(shè)備驅(qū)動開發(fā)設(shè)備驅(qū)動開發(fā)2010.3提綱提綱n 設(shè)備驅(qū)動概述n 如何編寫內(nèi)核模塊n 如何編寫設(shè)備驅(qū)動n 字符設(shè)備驅(qū)動實例預備知識:設(shè)備控制器的功能及接口預備知識:設(shè)備控制器的功能及接口n 每一個物理設(shè)備都有自己的硬件控制器,每一個硬件控制器都有自己的控制和狀態(tài)寄存器(CSR),不同的設(shè)備是不同的;n 設(shè)備控制器的功能:命令寄存器和譯碼器(接收和識別命令)、數(shù)據(jù)寄存器(數(shù)據(jù)交換)、狀態(tài)寄存器(設(shè)備狀態(tài)的了解和報告)、地址寄存器(地址識別)n 設(shè)備控制器與CPU的接口:數(shù)據(jù)線、地址線、控制線n 設(shè)備控制器與設(shè)備的接口:數(shù)據(jù)信號、狀態(tài)信號、控制信號預備知識:系統(tǒng)對設(shè)備的管理預備知識:系統(tǒng)對

2、設(shè)備的管理n 設(shè)備控制表(DCT):每個設(shè)備一張表,表結(jié)構(gòu)如下:設(shè)備標識符、設(shè)備類型、設(shè)備地址或設(shè)備號、設(shè)備狀態(tài)、等待隊列指針、重復執(zhí)行的次數(shù)和時間、I/O控制器指針n 系統(tǒng)設(shè)備表(SDT):整個系統(tǒng)一張表,表結(jié)構(gòu)如下:DCT指針、正在使用設(shè)備的進程標識、設(shè)備驅(qū)動程序入口驅(qū)動原理:什么是設(shè)備驅(qū)動?驅(qū)動原理:什么是設(shè)備驅(qū)動?n 設(shè)備控制器的CSR用于啟動和停止設(shè)備,初始化設(shè)備和診斷它的問題n 管理這些硬件控制器的代碼不是放在每一個應用程序里邊,而是放在Linux內(nèi)核中。這些處理或管理硬件控制器的軟件就叫做設(shè)備驅(qū)動程序。n 一般來說,一個驅(qū)動程序控制一種硬件設(shè)備;n Linux核心的設(shè)備驅(qū)動程序是

3、Linux操作系統(tǒng)的一部分,它運行于核心態(tài)。驅(qū)動原理:驅(qū)動架構(gòu)驅(qū)動原理:驅(qū)動架構(gòu)硬件設(shè)備驅(qū)動塊設(shè)備字符設(shè)備緩沖區(qū)文件子系統(tǒng)進程控制子系統(tǒng)進程調(diào)度內(nèi)存管理調(diào)進程間通訊系統(tǒng)調(diào)用接口硬件層內(nèi)核層內(nèi)核層用戶層庫函數(shù)用戶程序驅(qū)動原理:內(nèi)核何時使用驅(qū)動程序?驅(qū)動原理:內(nèi)核何時使用驅(qū)動程序?n Linux內(nèi)核使用“設(shè)備無關(guān)”的I/O子系統(tǒng)來為所有的設(shè)備服務。每個設(shè)備都提供標準接口給內(nèi)核,從而盡可能地隱藏了自己的特性。n 用戶程序使用一些基本的系統(tǒng)調(diào)用從設(shè)備讀取數(shù)據(jù)并且將它們存入緩沖。n 每當一個系統(tǒng)調(diào)用被使用時,內(nèi)核就轉(zhuǎn)到相應的設(shè)備驅(qū)動例程來操縱硬件。 驅(qū)動原理:驅(qū)動原理: Linux的的I/O子系統(tǒng)子系統(tǒng)

4、驅(qū)動原理:驅(qū)動原理:Linux設(shè)備與文件的關(guān)系設(shè)備與文件的關(guān)系n Linux將所有外部設(shè)備看成是一類特殊文件,稱之為“設(shè)備文件”,如果說系統(tǒng)調(diào)用是Linux內(nèi)核和應用程序之間的接口,那么設(shè)備驅(qū)動程序則可以看成是Linux內(nèi)核與外部設(shè)備之間的接口。n 設(shè)備驅(qū)動程序向應用程序屏蔽了硬件在實現(xiàn)上的細節(jié),使得應用程序可以像操作普通文件一樣來操作外部設(shè)備。驅(qū)動原理:設(shè)備文件驅(qū)動原理:設(shè)備文件nLinux抽象了對硬件的處理,使得所有的硬件設(shè)備都可以像普通文件一樣來看待:它們可以使用和操作文件相同的、標準的系統(tǒng)調(diào)用接口來完成打開、關(guān)閉、讀寫和I/O控制操作,而驅(qū)動程序的主要任務也就是要實現(xiàn)這些系統(tǒng)調(diào)用函數(shù)。

5、nLinux系統(tǒng)中的所有硬件設(shè)備都使用一個特殊的設(shè)備文件來表示,例如,系統(tǒng)中的第一個IDE硬盤在文件系統(tǒng)中使用/dev/hda表示。nLinux通過分配設(shè)備號來標識每個設(shè)備,每個設(shè)備文件對應有兩個設(shè)備號:一個是主設(shè)備號,標識該設(shè)備的種類,也標識了該設(shè)備所使用的驅(qū)動程序,有關(guān)主設(shè)備號分配見linux/include/linux/major.h 。例如軟驅(qū)主設(shè)備號是2,IDE硬盤的主設(shè)備號是3;另一個是次設(shè)備號,標識使用同一設(shè)備驅(qū)動程序的不同硬件設(shè)備。例如,一臺PC有兩塊IDE硬盤,它們的主設(shè)備號都是 3,但是第一個硬盤的次設(shè)備號為1,另一個次設(shè)備號為2。設(shè)備文件的主設(shè)備號必須與設(shè)備驅(qū)動程序在登記

6、該設(shè)備時申請的主設(shè)備號一致,否則用戶進程將無法訪問到設(shè)備驅(qū)動程序。驅(qū)動原理:設(shè)備驅(qū)動程序的主要功能驅(qū)動原理:設(shè)備驅(qū)動程序的主要功能n 對設(shè)備進行初始化;n 使設(shè)備投入運行和退出服務;n 從設(shè)備接收數(shù)據(jù)并將它們送回內(nèi)核;n 將數(shù)據(jù)從內(nèi)核送到設(shè)備;n 檢測和處理設(shè)備出現(xiàn)的錯誤。驅(qū)動原理:字符設(shè)備和塊設(shè)備驅(qū)動原理:字符設(shè)備和塊設(shè)備n 在Linux操作系統(tǒng)下有兩類主要的設(shè)備文件:一類是字符設(shè)備,另一類則是塊設(shè)備。n 字符設(shè)備是以字節(jié)為單位逐個進行I/O操作的設(shè)備,在對字符設(shè)備發(fā)出讀寫請求時,實際的硬件I/O緊接著就發(fā)生了,一般來說字符設(shè)備中的緩存是可有可無的,而且也不支持隨機訪問。如鼠標、鍵盤、串口

7、n 塊設(shè)備則是利用一塊系統(tǒng)內(nèi)存作為緩沖區(qū),當用戶進程對設(shè)備進行讀寫請求時,驅(qū)動程序先查看緩沖區(qū)中的內(nèi)容,如果緩沖區(qū)中的數(shù)據(jù)能滿足用戶的要求就返回相應的數(shù)據(jù),否則就調(diào)用相應的請求函數(shù)來進行實際的I/O操作。如硬盤、光驅(qū)等塊設(shè)備主要是針對磁盤等慢速設(shè)備設(shè)計的,其目的是避免耗費過多的CPU時間來等待操作的完成。驅(qū)動原理:設(shè)備文件與驅(qū)動程序的關(guān)系驅(qū)動原理:設(shè)備文件與驅(qū)動程序的關(guān)系n 所有已經(jīng)注冊(即已經(jīng)加載了驅(qū)動程序)的硬件設(shè)備的主設(shè)備號可以從/proc/devices文件中得到。n 使用mknod命令可以創(chuàng)建指定類型的設(shè)備文件,同時為其分配相應的主設(shè)備號和次設(shè)備號。mknod /dev/device

8、_name device_type major_number minor_number其中:device_name是此設(shè)備的文件device_type是此設(shè)備的類型,c表示字符設(shè)備,b表示塊設(shè)備例如,建立一個主設(shè)備號為6,次設(shè)備號為0的字符設(shè)備文件/dev/lp0 :# mknod /dev/lp0 c 6 0 n 當應用程序?qū)δ硞€設(shè)備文件進行系統(tǒng)調(diào)用時,Linux內(nèi)核會根據(jù)該設(shè)備文件的設(shè)備類型和主設(shè)備號調(diào)用相應的驅(qū)動程序,并從用戶態(tài)進入到核心態(tài),再由驅(qū)動程序判斷該設(shè)備的次設(shè)備號,最終完成對相應硬件的操作。驅(qū)動機制:設(shè)備管理驅(qū)動機制:設(shè)備管理n 1、設(shè)備管理:系統(tǒng)對已經(jīng)登記的字符設(shè)備的管理是由

9、chrdevs來管理的。n Linux對設(shè)備進行訪問時,訪問文件系統(tǒng)中相應的設(shè)備文件,通過文件系統(tǒng)和文件的屬性描述塊,可以找到該文件系統(tǒng)或設(shè)備對應的設(shè)備號。在實際訪問列表時,以chrdevsMAJORMINOR 形式訪問。static struct char_device_struct struct char_device_struct *next;unsigned int major;unsigned int baseminor;int minorct;const char *name;struct *fops;struct cdev *cdev;/* will die */ *chrdev

10、sMAX_PROBE_HASH;驅(qū)動機制:驅(qū)動機制:I/O請求管理請求管理n 2、I/O請求管理:系統(tǒng)會把一部分系統(tǒng)內(nèi)存作為塊設(shè)備驅(qū)動程序與文件系統(tǒng)接口之間的一層緩沖區(qū),每個緩沖區(qū)與某臺設(shè)備中的特定區(qū)域相聯(lián)系,文件系統(tǒng)首先試圖查找相應的緩沖區(qū),如未找到才向該設(shè)備發(fā)出I/O讀寫請求,由設(shè)備驅(qū)動程序?qū)@些請求進行處理。驅(qū)動機制:中斷請求驅(qū)動機制:中斷請求n 3、中斷請求:設(shè)備進行實際的輸入/輸出時,如果時間過長而始終占用CPU,就會影響系統(tǒng)的效率,所以Linux采用中斷的機制。驅(qū)動機制:中斷與輪詢驅(qū)動機制:中斷與輪詢n設(shè)備被執(zhí)行某個命令時,如“將讀取磁頭移動到軟盤的第42扇區(qū)上”,設(shè)備驅(qū)動可以從輪

11、詢方式和中斷方式中選擇一種以判斷設(shè)備是否已經(jīng)完成此命令。n輪詢方式意味著需要經(jīng)常讀取設(shè)備的狀態(tài),一直到設(shè)備狀態(tài)表明請求已經(jīng)完成為止。如果設(shè)備驅(qū)動被連接進入內(nèi)核,這時使用輪詢方式將會帶來災難性后果:內(nèi)核將在此過程中無所事事,直到設(shè)備完成此請求。但是輪詢設(shè)備驅(qū)動可以通過使用系統(tǒng)定時器,使核心周期性調(diào)用設(shè)備驅(qū)動中的某個例程來檢查設(shè)備狀態(tài)。 定時器過程可以檢查命令狀態(tài)及Linux軟盤驅(qū)動的工作情況。使用定時器是輪詢方式中最好的一種,但更有效的方法是使用中斷。 n基于中斷的設(shè)備驅(qū)動會在它所控制的硬件設(shè)備需要服務時引發(fā)一個硬件中斷。如以太網(wǎng)設(shè)備驅(qū)動從網(wǎng)絡上接收到一個以太數(shù)據(jù)報時都將引起中斷。Linux內(nèi)核

12、需要將來自硬件設(shè)備的中斷傳遞到相應的設(shè)備驅(qū)動。這個過程由設(shè)備驅(qū)動向內(nèi)核注冊其使用的中斷來協(xié)助完成。此中斷處理例程的地址和中斷號都將被記錄下來。在/proc/interrupts文件中你可以看到設(shè)備驅(qū)動所對應的中斷號及類型。驅(qū)動機制:高速緩沖驅(qū)動機制:高速緩沖n 4、高速緩沖區(qū):為了加速物理設(shè)備的訪問速度,Linux將塊緩沖區(qū)放在cache內(nèi),塊緩沖區(qū)是由buff_head 連成的鏈表結(jié)構(gòu)。struct buff_head unsigned long b_blocknr 邏輯塊號unsigned long b_size 塊大小kdev_t b_dev 虛擬設(shè)備標志符kdev_t b_rdev 實

13、際設(shè)備標志符unsigned long b_state 緩沖區(qū)狀態(tài)標志char * b_data 指向緩沖區(qū)的指針unsigned long b_rsector 實際設(shè)備上的塊號.驅(qū)動機制:驅(qū)動機制: DMAn 有時設(shè)備與系統(tǒng)要進行大批量的數(shù)據(jù)傳輸,采用中斷的方式會給CPU帶來很大的負荷,降低了系統(tǒng)的效率。n DMA操作是外設(shè)直接與內(nèi)存進行數(shù)據(jù)傳輸,而無須CPU的干預。如硬盤數(shù)據(jù)傳輸、網(wǎng)卡數(shù)據(jù)傳輸?shù)?。n DMA控制器沒有任何虛擬內(nèi)存的概念,它只存取系統(tǒng)中的物理內(nèi)存,不能在進程虛擬地址空間內(nèi)直接使用DMA。驅(qū)動接口:設(shè)備驅(qū)動程序與外界的接口驅(qū)動接口:設(shè)備驅(qū)動程序與外界的接口n 每種類型的驅(qū)動程序

14、,不管是字符設(shè)備還是塊設(shè)備都為內(nèi)核提供相同的調(diào)用接口,因此內(nèi)核能以相同的方式處理不同的設(shè)備。Linux為每種不同類型的設(shè)備驅(qū)動程序維護相應的數(shù)據(jù)結(jié)構(gòu),以便定義統(tǒng)一的接口并實現(xiàn)驅(qū)動程序的可裝載性和動態(tài)性,n Linux設(shè)備驅(qū)動程序與外界的接口分為3部分:驅(qū)動程序與內(nèi)核的接口,這是通過數(shù)據(jù)結(jié)構(gòu)來完成的;驅(qū)動程序與系統(tǒng)引導的接口,這部分利用驅(qū)動程序?qū)υO(shè)備進行初始化;設(shè)備驅(qū)動程序與設(shè)備的接口,這部分描述了驅(qū)動程序如何與設(shè)備進行交互,這與具體設(shè)備密切相關(guān);它們之間的關(guān)系如下頁圖所示:驅(qū)動接口:驅(qū)動程序與外界的接口驅(qū)動接口:驅(qū)動程序與外界的接口 設(shè)備驅(qū)動程序設(shè)備驅(qū)動程序 操作系統(tǒng)內(nèi)核操作系統(tǒng)內(nèi)核 接口 數(shù)

15、據(jù)結(jié)構(gòu)數(shù)據(jù)結(jié)構(gòu) 實現(xiàn)實現(xiàn) 系統(tǒng)引導系統(tǒng)引導 接口 各設(shè)備各設(shè)備 初始化初始化 具體設(shè)備具體設(shè)備 接口 進行交互進行交互 驅(qū)動程序與設(shè)備間驅(qū)動程序與設(shè)備間 圖 設(shè)備驅(qū)動程序與外界的接口 設(shè)備驅(qū)動程序的特點設(shè)備驅(qū)動程序的特點(1)內(nèi)核代碼:設(shè)備驅(qū)動程序是內(nèi)核的一部分,如果驅(qū)動程序出錯,則可能導致系統(tǒng)崩潰。 (2)內(nèi)核接口:設(shè)備驅(qū)動程序必須為內(nèi)核或者其子系統(tǒng)提供一個標準接口。比如,一個終端驅(qū)動程序必須為內(nèi)核提供一個文件I/O接口;一個SCSI設(shè)備驅(qū)動程序應該為SCSI子系統(tǒng)提供一個SCSI設(shè)備接口,同時SCSI子系統(tǒng)也必須為內(nèi)核提供文件的I/O接口及緩沖區(qū)。 (3)內(nèi)核機制和服務:設(shè)備驅(qū)動程序使用一

16、些標準的內(nèi)核服務,如內(nèi)存分配等。 (4)可裝載:大多數(shù)的Linux操作系統(tǒng)設(shè)備驅(qū)動程序都可以在需要時裝載進內(nèi)核,在不需要時從內(nèi)核中卸載。 (5)可設(shè)置:Linux 操作系統(tǒng)設(shè)備驅(qū)動程序可以集成為內(nèi)核的一部分,并可以根據(jù)需要把其中的某一部分集成到內(nèi)核中,這只需要在系統(tǒng)編譯時進行相應的設(shè)置即可。 (6)動態(tài)性:在系統(tǒng)啟動且各個設(shè)備驅(qū)動程序初始化后,驅(qū)動程序?qū)⒕S護其控制的設(shè)備。如果該設(shè)備驅(qū)動程序控制的設(shè)備不存在也不影響系統(tǒng)的運行,那么此時的設(shè)備驅(qū)動程序只是多占用了一點系統(tǒng)內(nèi)存罷了思考思考n 設(shè)備驅(qū)動與文件的關(guān)系?提綱提綱n 設(shè)備驅(qū)動概述n 如何編寫內(nèi)核模塊n 如何編寫設(shè)備驅(qū)動n 字符設(shè)備驅(qū)動實例內(nèi)核

17、模塊內(nèi)核模塊n在開始編寫設(shè)備驅(qū)動之前,先需要了解內(nèi)核模塊的概念。Linux內(nèi)核是一個整體的結(jié)構(gòu),因此向內(nèi)核添加任何東西或者刪除某些功能,都十分困難。為了解決這個問題,Linux引入了內(nèi)核模塊機制,從而可以動態(tài)地在內(nèi)核中添加或者刪除模塊nLinux內(nèi)核中采用可加載的模塊化設(shè)計 (LKMs, Loadable Kernel Modules) ,一般情況下編譯的 Linux 內(nèi)核是支持可插入式模塊的,也就是將最基本的核心代碼編譯在內(nèi)核中,其他的代碼可以選擇在內(nèi)核中,或者編譯為內(nèi)核的模塊文件。 n常見的驅(qū)動程序也是作為內(nèi)核模塊動態(tài)加載的,比如聲卡驅(qū)動和網(wǎng)卡驅(qū)動等,而 Linux最基礎(chǔ)的驅(qū)動,如CPU、

18、PCI總線、TCP/IP協(xié)議、APM(高級電源管理)、VFS等驅(qū)動程序則直接編譯在內(nèi)核文件中。有時也把內(nèi)核模塊叫做驅(qū)動程序,只不過驅(qū)動的內(nèi)容不一定是硬件罷了,比如 ext3 文件系統(tǒng)的驅(qū)動。因此,加載驅(qū)動時就是加載內(nèi)核模塊。n模塊不被編譯在內(nèi)核中,因而控制了內(nèi)核的大小。然而模塊一旦被插入內(nèi)核,它就和內(nèi)核其他部分一樣。這樣一來就會增加一部分系統(tǒng)開銷。同時,如果模塊出現(xiàn)問題,也許會帶來系統(tǒng)崩潰。所以在進行模塊開發(fā)時,要特別注意與應用程序開發(fā)的區(qū)別。n 常用的模塊命令如下:lsmod顯示當前系統(tǒng)已經(jīng)加載的模塊rmmod是用于將當前模塊卸載。insmod 和 modprobe 是用于加載當前模塊但 i

19、nsmod 不會自動解決依存關(guān)系而modprobe則可以根據(jù)模塊間依存關(guān)系以及/etc/modules.conf文件中的內(nèi)容自動插入模塊。n mknod是用于創(chuàng)建設(shè)備節(jié)點。如果需要加載的模塊是一個設(shè)備驅(qū)動模塊,它占用一個主設(shè)備號,那么在加載此模塊前,必須先為它在文件系統(tǒng)中建立好相應的設(shè)備節(jié)點 。n 例如:為設(shè)備驅(qū)動模塊test.ko建立字符設(shè)備節(jié)點test,其主設(shè)備號為254,次設(shè)備為0n mknod /dev/test c 254 0舉例:簡單的內(nèi)核模塊舉例:簡單的內(nèi)核模塊下面舉一個簡單的內(nèi)核模塊例子,以此引出內(nèi)核的機制和框架:/* hello.c */#include #include #

20、include static int hello_init(void)printk(KERN_ALERT Hello, worldn);return 0;static void hello_exit(void)printk(KERN_ALERT Goodbye, cruel worldn);module_init(hello_init);module_exit(hello_exit);MODULE_LICENSE(GPL);編譯模塊編譯模塊n 下面是一個典型的2.6內(nèi)核下模塊的Makefile#Make hello.c file#KDIR:=/lib/modules/$(shell uname

21、 -r)/buildobj-m:=hello.odefault: $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modulesclean: $(RM) .*.cmd *.mod.c *.o *.ko -r .tmp*n 執(zhí)行make即可生成hello.ko測試模塊測試模塊1、以root用戶登錄,運行tail -f /var/log/messages2、在另外一個終端以root用戶登錄,裝載模塊insmod ./hello.ko3、這時會在/var/log/messages看到如下信息:Mar 25 16:50:51 wpon kernel: Hello, world4、卸

22、載模塊Now remove the module:rmmod hello5、這時會在/var/log/messages看到如下信息:Mar 25 16:51:08 wpon kernel: Goodbye, cruel world模塊機制模塊機制n從上面的例子可以看出,加載模塊的方式與以往的應用程序開發(fā)有很大的不同。以往在開發(fā)應用程序時都有一個 main函數(shù)作程序的入口點,而在模塊開發(fā)時卻沒有 main 函數(shù),模塊在調(diào)用 insmod命令時被加載,此時的入口點是module_init函數(shù)。同樣,模塊在調(diào)用 rmmod函數(shù)時被卸載,此時的出口點是module_exit函數(shù)。模塊初始化時執(zhí)行mod

23、ule_init入口點模塊不再使用時(手工或關(guān)機時)執(zhí)行module_exit出口點編寫模塊的規(guī)則編寫模塊的規(guī)則n 不能訪問C庫如 printf(), strcat(),只能使用內(nèi)核提供的頭文件n 內(nèi)核有自己的C功能函數(shù),如類似于printf()的printk()n 只能使用內(nèi)核提供的頭文件n 不要使用浮點數(shù)n 聲明變量為static的,防止命名空間污染模塊調(diào)試技術(shù)模塊調(diào)試技術(shù)n 內(nèi)核對調(diào)試的支持:配置內(nèi)核時make menuconfig注意選擇debug,編程時CONFIG_DEBUG_INFO等宏的引用n printk及信息顯示級別n /proc系統(tǒng)提供的信息printkn就如同在編寫用戶

24、空間的應用程序,打印信息有時是很好的調(diào)試手段,也是在代碼中很常用的組成部分。但是與用戶空間不同,在內(nèi)核空間要用函數(shù) printk 而不能用平常的函數(shù)printf。printk和 printf很類似,都可以按照一定的格式打印消息,所不同的是,printk還可以定義打印消息的優(yōu)先級。這些不同優(yōu)先級的信息可以輸出到控制臺上、/var/log/messages里。其中,對輸出給控制臺的信息有一個特定的優(yōu)先級console_loglevel。若優(yōu)先級小于這個整數(shù)值時,則消息才能顯示到控制臺上,否則,消息會顯示在/var/log/messages 里。若不加任何優(yōu)先級選項,則消息默認輸出到/var/log

25、/messages文件中。 printkn include/linux/kernel.h下對不同日志級別的定義#define KERN_EMERG /* system is unusable */#define KERN_ALERT /* action must be taken immediately */#define KERN_CRIT /* critical conditions */#define KERN_ERR /* error conditions */#define KERN_WARNING /* warning conditions */#define KERN_NOTIC

26、E /* normal but significant condition */#define KERN_INFO /* informational */#define KERN_DEBUG /* debug-level messages */n 優(yōu)先級console_loglevel被指定為18之間的整數(shù)值。如果設(shè)定為1,則只有級別為0的信息才能到達控制臺。如果設(shè)定為8,則包括調(diào)試信息在內(nèi)的所有消息都顯示出來/procn /proc 文件系統(tǒng)是一個偽文件系統(tǒng),它是一種內(nèi)核和內(nèi)核模塊用來向進程發(fā)送信息的機制。這個偽文件系統(tǒng)讓用戶可以和內(nèi)核內(nèi)部數(shù)據(jù)結(jié)構(gòu)進行交互,獲取有關(guān)進程的有用信息,在運行時通

27、過改變內(nèi)核參數(shù)改變設(shè)置。與其他文件系統(tǒng)不同,/proc存在于內(nèi)存之中而不是硬盤上,在加載模塊成功后,可以使用查看/proc/device文件獲得相關(guān)設(shè)備的主設(shè)備號。 思考思考n 內(nèi)核何時調(diào)用模塊?lsmod和/proc/modules當內(nèi)核需要的某功能不存在時通過運行modprobe調(diào)入相應模塊,modprobe運行前檢查依賴關(guān)系/lib/modules/$(shell uname -r)/modules.depinsmodrmmodn 模塊是如何工作的?通過module_init告訴內(nèi)核本模塊提供的操作函數(shù)當內(nèi)核需要操作模塊時則調(diào)用模塊提供的函數(shù)模塊不再使用時執(zhí)行module_exit,手工

28、或關(guān)機時提綱提綱n 設(shè)備驅(qū)動概述n 如何編寫內(nèi)核模塊n 如何編寫設(shè)備驅(qū)動n 字符設(shè)備驅(qū)動實例設(shè)備驅(qū)動編寫流程設(shè)備驅(qū)動編寫流程 n在上一節(jié)中已經(jīng)提到,模塊在調(diào)用 insmod命令時被加載,此時的入口點是module_init函數(shù),通常在該函數(shù)中完成設(shè)備的注冊。同樣,模塊在調(diào)用 rmmod函數(shù)時被卸載,此時的入口點是module_exit函數(shù),在該函數(shù)中完成設(shè)備的卸載。在設(shè)備完成注冊加載之后,用戶的應用程序就可以對該設(shè)備進行一定的操作,如read、write等,而驅(qū)動程序就是用于實現(xiàn)這些操作,在用戶應用程序調(diào)用相應入口函數(shù)時執(zhí)行相關(guān)的操作。設(shè)備驅(qū)動程序流程圖如下所示:驅(qū)動接口:向內(nèi)核提供的設(shè)備操作

29、接口驅(qū)動接口:向內(nèi)核提供的設(shè)備操作接口nLinux中的I/O子系統(tǒng)向內(nèi)核中的其他部分提供了一個統(tǒng)一的標準設(shè)備接口,這是通過include/linux/fs.h中的數(shù)據(jù)結(jié)構(gòu)來完成的:struct loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char *, size_t, loff_t *); ssize_t (*write) (struct file *, const char *, size_t, loff_t *); unsigned int (*poll) (struct fi

30、le *, struct poll_table_struct *); int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); int (*open) (struct inode *, struct file *); int (*release) (struct inode *, struct file *); ; n這個結(jié)構(gòu)的每一個成員的名字都對應著一個系統(tǒng)調(diào)用.用戶進程利用系統(tǒng)調(diào)用在對設(shè)備文件

31、進行如read/write操作時,系統(tǒng)調(diào)用通過設(shè)備文件的主設(shè)備號找到相應的設(shè)備驅(qū)動程序,然后讀取這個數(shù)據(jù)結(jié)構(gòu)相應的函數(shù)指針,接著把控制權(quán)交給該函數(shù).例如,當應用程序?qū)υO(shè)備文件執(zhí)行讀操作時,內(nèi)核將調(diào)用結(jié)構(gòu)中的read函數(shù)。驅(qū)動結(jié)構(gòu):驅(qū)動程序的結(jié)構(gòu)驅(qū)動結(jié)構(gòu):驅(qū)動程序的結(jié)構(gòu)n Linux的設(shè)備驅(qū)動程序大致可以分為如下幾個部分: 驅(qū)動程序的注冊與注銷 設(shè)備的打開與釋放 設(shè)備的讀寫操作 設(shè)備的控制操作 設(shè)備的中斷和輪詢處理驅(qū)動結(jié)構(gòu):驅(qū)動程序的注冊與注銷驅(qū)動結(jié)構(gòu):驅(qū)動程序的注冊與注銷 n舊方式:向系統(tǒng)增加一個驅(qū)動程序意味著要賦予它一個主設(shè)備號,這可以通過在驅(qū)動程序的初始化過程中調(diào)用register_chr

32、dev( )或者register_blkdev( )來完成。而在關(guān)閉字符設(shè)備或者塊設(shè)備時,則需要通過調(diào)用unregister_chrdev( )或unregister_blkdev( )從內(nèi)核中注銷設(shè)備,同時釋放占用的主設(shè)備號。 n新方式:#include n注冊:void cdev_init(struct cdev *dev,struct *fops);int cdev_add(struct cdev *dev,dev_t num,unsigned int count);n注銷:void cdev_del(struct cdev *dev);驅(qū)動結(jié)構(gòu):設(shè)備的打開與釋放驅(qū)動結(jié)構(gòu):設(shè)備的打開與釋

33、放 n 打開設(shè)備是通過調(diào)用結(jié)構(gòu)中的函數(shù)open( )來完成的,它是驅(qū)動程序用來為今后的操作完成初始化準備工作的。在大部分驅(qū)動程序中,open( )通常需要完成下列工作:1.檢查設(shè)備相關(guān)錯誤,如設(shè)備尚未準備好等。2.如果是第一次打開,則初始化硬件設(shè)備。3.識別次設(shè)備號,如果有必要則更新讀寫操作的當前位置指針f_ops。4.分配和填寫要放在file-private_data里的數(shù)據(jù)結(jié)構(gòu)。n 釋放設(shè)備是通過調(diào)用結(jié)構(gòu)中的函數(shù)release( )來完成的,這個設(shè)備方法有時也被稱為close( ),它的作用正好與open( )相反,通常要完成下列工作: 1.釋放在file-private_data中分配的

34、內(nèi)存。2.關(guān)閉設(shè)備。驅(qū)動結(jié)構(gòu):設(shè)備的讀寫操作驅(qū)動結(jié)構(gòu):設(shè)備的讀寫操作 n 字符設(shè)備的讀寫操作相對比較簡單,直接使用函數(shù)read( )和write( )。n 如果是塊設(shè)備,則需要調(diào)用函數(shù)block_read( )和block_write( )來進行數(shù)據(jù)讀寫,這兩個函數(shù)將向設(shè)備請求表中增加讀寫請求,以便Linux內(nèi)核可以對請求順序進行優(yōu)化。由于是對內(nèi)存緩沖區(qū)而不是直接對設(shè)備進行操作的,因此能很大程度上加快讀寫速度。如果內(nèi)存緩沖區(qū)中沒有所要讀入的數(shù)據(jù),或者需要執(zhí)行寫操作將數(shù)據(jù)寫入設(shè)備,那么就要執(zhí)行真正的數(shù)據(jù)傳輸,這是通過調(diào)用數(shù)據(jù)結(jié)構(gòu)blk_dev_struct中的函數(shù)request_fn( )來完成

35、的。 nssize_t read(struct file *filp,char _user *buff,size_t count, loff_t *offp);nssize_t write(struct file *filp,const char _user *buff,size_t count, loff_t *offp);驅(qū)動結(jié)構(gòu):設(shè)備的讀寫操作注意驅(qū)動結(jié)構(gòu):設(shè)備的讀寫操作注意n在read和write參數(shù)里的_user表明,這個緩沖是用戶空間的指針,因此它不能直接被內(nèi)核代碼復引用。原因如下:在內(nèi)核執(zhí)行的過程中,用戶空間的指針也許還是無效的(如:還沒有給這個地址映射,或者它指向別的空間,或者

36、是隨機的數(shù)據(jù));即使這個指針指的就是內(nèi)核空間的同一空間,但是用戶空間是基于頁的,而在這里討論的這個內(nèi)存空間在系統(tǒng)調(diào)用執(zhí)行的時候也許還不在RAM中。直接引用用戶空間的內(nèi)存,會產(chǎn)生缺頁錯誤;用戶空間提供的這個指針也許會有錯誤,也許是有惡意的。這樣如果驅(qū)動程序盲目地引用用戶空間的指針,就會給應用程序提供一個訪問內(nèi)核空間的后門。n因此,驅(qū)動程序需要使用內(nèi)核提供的特別函數(shù)控制這些對指針的訪問,以完成任務,這可以通過提供的內(nèi)核函數(shù),在這里就要使用 copy_to_user 或copy_from_user 函數(shù),它們就是用來實現(xiàn)用戶空間和內(nèi)核空間的數(shù)據(jù)交換的驅(qū)動結(jié)構(gòu):設(shè)備的控制操作驅(qū)動結(jié)構(gòu):設(shè)備的控制操作

37、n 除了讀寫操作外,應用程序有時還需要對設(shè)備進行控制,這可以通過設(shè)備驅(qū)動程序中的函數(shù)ioctl( )來完成。n ioctl是設(shè)備驅(qū)動程序中對設(shè)備的I/O通道進行管理的函數(shù)。所謂對I/O通道進行管理,就是對設(shè)備的一些特性進行控制,例如串口的傳輸波特率、馬達的轉(zhuǎn)速等等。n ioctl( )的用法與具體設(shè)備密切關(guān)聯(lián),因此需要根據(jù)設(shè)備的實際情況進行具體分析。n 設(shè)備驅(qū)動程序的ioctl函數(shù):int (*ioctl) (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); 命令號:類型、序號、方向、大小

38、。include/asm/ioctl.hDocumentation/ioctl-number.txt驅(qū)動結(jié)構(gòu):用戶對設(shè)備的控制操作驅(qū)動結(jié)構(gòu):用戶對設(shè)備的控制操作n 在用戶空間內(nèi)調(diào)用的ioctl函數(shù):int ioctl(int fd, unsigned long cmd, .);n fd就是用戶程序打開設(shè)備時使用open函數(shù)返回的文件標識符,cmd就是用戶程序?qū)υO(shè)備的控制命令,后面的省略號,那是一些補充參數(shù),一般最多一個,有或沒有是和cmd的意義相關(guān)的。驅(qū)動模塊:驅(qū)動模塊說明驅(qū)動模塊:驅(qū)動模塊說明n Linux下的設(shè)備驅(qū)動程序可以按照兩種方式進行編譯,一種是直接靜態(tài)編譯成內(nèi)核的一部分,另一種則是

39、編譯成可以動態(tài)加載的模塊。如果編譯進內(nèi)核的話,會增加內(nèi)核的大小,還要改動內(nèi)核的源文件,而且不能動態(tài)地卸載,不利于調(diào)試,所以推薦使用模塊方式。n 從本質(zhì)上來講,模塊也是內(nèi)核的一部分,它不同于普通的應用程序,不能調(diào)用位于用戶態(tài)下的C或者C+庫函數(shù),而只能調(diào)用Linux內(nèi)核提供的函數(shù),在/proc/kallsyms中可以查看到內(nèi)核提供的所有函數(shù)。驅(qū)動模塊:模塊化驅(qū)動的編寫說明驅(qū)動模塊:模塊化驅(qū)動的編寫說明n 在以模塊方式編寫驅(qū)動程序時,要實現(xiàn)兩個必不可少的函數(shù)module_init ( )和module_exit( ),而且至少要包含兩個頭文件(、 )。n 編譯生成的模塊(一般為.ko文件)可以使用

40、命令insmod載入Linux內(nèi)核,從而成為內(nèi)核的一個組成部分,此時內(nèi)核會調(diào)用模塊中的函數(shù)module_init ( )。當不再需要該模塊時,可以使用rmmod命令進行卸載,此時內(nèi)核會調(diào)用模塊中的函數(shù)module_exit( )。任何時候都可以使用命令lsmod查看目前已經(jīng)加載的模塊以及正在使用該模塊的用戶數(shù)。 思考思考n register_chrdev ()與misc_register ()有何聯(lián)系與區(qū)別?misc_register ()用來注冊其他類型設(shè)備的,它的主設(shè)備是10調(diào)用register_chrdev(),設(shè)備名和函數(shù)指針通過miscdevice結(jié)構(gòu)獲得,同時結(jié)構(gòu)體還保存設(shè)備的次設(shè)

41、備號提綱提綱n 設(shè)備驅(qū)動概述n 如何編寫內(nèi)核模塊n 如何編寫設(shè)備驅(qū)動n 字符設(shè)備驅(qū)動實例基于字符設(shè)備的基于字符設(shè)備的LinuxLinux驅(qū)動程序設(shè)計實例驅(qū)動程序設(shè)計實例n 實驗目的掌握Linux設(shè)備模塊驅(qū)動開發(fā)的方法掌握Linux字符設(shè)備驅(qū)動的開發(fā)n 實驗內(nèi)容調(diào)試一個簡單字符設(shè)備驅(qū)動程序用提供的應用程序測試驅(qū)動程序n 下面舉一個虛擬的字符驅(qū)動程序舉例基于內(nèi)存拷貝的簡單字符驅(qū)動程序設(shè)計。實驗步驟實驗步驟n編寫字符驅(qū)動程序(test.c); 確定設(shè)備名稱與主設(shè)備號根據(jù)需要編寫設(shè)備文件對內(nèi)核上層的接口,比如open, release, read, write, ioctl等(test.c);n寫Ma

42、kefile,編譯生成模塊目標文件(test.ko)n用insmod將動態(tài)加載驅(qū)動模塊n在目錄/dev下用mknod命令建立相應的設(shè)備文件n在用戶態(tài)下編寫應用程序測試,使用該設(shè)備驅(qū)動 (mytest.c)n編譯運行測試用例驗證驅(qū)動正確性字符驅(qū)動程序代碼字符驅(qū)動程序代碼test.c#include #include #include #include #include #include #include #include #include #include #include MODULE_LICENSE(Dual BSD/GPL);#define DEVICE_NAME teststatic u

43、nsigned int test_major = 0;字符驅(qū)動程序代碼字符驅(qū)動程序代碼test.cstatic int read_test(struct file * *buf,size_t count,loff_t *ppos) int left; if (access_ok(VERIFY_WRITE,buf,count) = -EFAULT ) return -EFAULT; for(left = count ; left 0 ; left-) _put_user(1,buf); buf+; return count;static int write_test(struct file * *buf,size_t count,loff_t *ppos) return count;將獲得的數(shù)據(jù)送到用戶空間中檢查用戶空間中的內(nèi)存塊是否可用字符驅(qū)動程序代碼字符驅(qū)動程序代碼test.cstatic int open_test(struct inode * inode , struct file

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論