Linux 字元裝置驅動程式的編寫框架

2021-05-22 08:40:47 字數 3668 閱讀 7879

一、linux device driver 的概念

系統呼叫是作業系統核心和應用程式之間的介面,裝置驅動程式是作業系統核心和機器硬體之間的介面。裝置驅動程式為應用程式遮蔽了硬體的細節,這樣在應用程 序看來,硬體裝置只是乙個裝置檔案,應用程式可以象操作普通檔案一樣對硬體裝置進行操作。裝置驅動程式是核心的一部分,它完成以下的功能:

1、對裝置初始化和釋放;

2、把資料從核心傳送到硬體和從硬體讀取資料;

3、讀取應用程式傳送給裝置檔案的資料和回送應用程式請求的資料;

4、檢測和處理裝置出現的錯誤。

在linux作業系統下有三類主要的裝置檔案型別,一是字元裝置,二是塊裝置,三是網路裝置。字元裝置和塊裝置的主要區別是:在對字元裝置發出讀/寫請求 時,實際的硬體i/o一般就緊接著發生了,塊裝置則不然,它利用一塊系統記憶體作緩衝區,當使用者程序對裝置請求能滿足使用者的要求,就返回請求的資料,如果不 能,就呼叫請求函式來進行實際的i/o操作。塊裝置是主要針對磁碟等慢速裝置設計的,以免耗費過多的cpu時間來等待。

已經提到,使用者程序是通過裝置檔案來與實際的硬體打交道。每個裝置檔案都都有其檔案屬性(c/b),表示是字元裝置還是塊裝置?另外每個檔案都有兩個裝置 號,第乙個是主裝置號,標識驅動程式,第二個是從裝置號,標識使用同乙個裝置驅動程式的不同的硬體裝置,比如有兩個軟盤,就可以用從裝置號來區分他們。設 備檔案的的主裝置號必須與裝置驅動程式在登記時申請的主裝置號一致,否則使用者程序將無法訪問到驅動程式。

最後必須提到的是,在使用者程序呼叫驅動程式時,系統進入核心態,這時不再是搶先式排程。也就是說,系統必須在你的驅動程式的子函式返回後才能進行其他的工 作。如果你的驅動程式陷入死迴圈,不幸的是你只有重新啟動機器了,然後就是漫長的fsck。

二、例項剖析

我們來寫乙個最簡單的字元裝置驅動程式。雖然它什麼也不做,但是通過它可以了解linux的裝置驅動程式的工作原理。把下面的c**輸入機器,你就會獲得 乙個真正的裝置驅動程式。

由於使用者程序是通過裝置檔案同硬體打交道,對裝置檔案的操作方式不外乎就是一些系統呼叫,如 open,read,write,close…, 注意,不是fopen, fread,但是如何把系統呼叫和驅動程式關聯起來呢?這需要了解乙個非常關鍵的資料結構:

struct file_operations

這個結構的每乙個成員的名字都對應著乙個系統呼叫。使用者程序利用系統呼叫在對裝置檔案進行諸如 read/write操作時,系統呼叫通過裝置檔案的主裝置號找到相應的裝置驅動程式,然後讀取這個資料結構相應的函式指標,接著把控制權交給該函式。這 是linux的裝置驅動程式工作的基本原理。既然是這樣,則編寫裝置驅動程式的主要工作就是編寫子函式,並填充file_operations的各個域。

下面就開始寫子程式。

#include 基本的型別定義

#include 檔案系統使用相關的標頭檔案

#include

#include

#include

unsigned int test_major = 0;

static int read_test(struct inode *inode,struct file *file,char *buf,int count)

return count;

} 這個函式是為read呼叫準備的。當呼叫read時,read_test()被呼叫,它把使用者的緩衝區全部寫 1。buf 是read呼叫的乙個引數。它是使用者程序空間的乙個位址。但是在read_test被呼叫時,系統進入核心態。所以不能使用buf這個位址,必須用 __put_user(),這是kernel提供的乙個函式,用於向使用者傳送資料。另外還有很多類似功能的函式。請參考,在向使用者空間拷貝資料之前,必須 驗證buf是否可用。這就用到函式verify_area。為了驗證buf是否可以用。

static int write_test(struct inode *inode,struct file *file,const char *buf,int count)

static int open_test(struct inode *inode,struct file *file )

static void release_test(struct inode *inode,struct file *file )

這幾個函式都是空操作。實際呼叫發生時什麼也不做,他們僅僅為下面的結構提供函式指標。

struct file_operations test_fops = ; 

裝置驅動程式的主體可以說是寫好了。現在要把驅動程式嵌入核心。驅動程式可以按照兩種方式編譯。一種是編譯進kernel,另一種是編譯成模組 (modules),如果編譯進核心的話,會增加核心的大小,還要改動核心的原始檔,而且不能動態的解除安裝,不利於除錯,所以推薦使用模組方式。

int init_module(void)

if (test_major == 0) test_major = result; /* dynamic */

return 0;

}  在用insmod命令將編譯好的模組調入記憶體時,init_module 函式被呼叫。在這裡,init_module只做了一件事,就是向系統的字元裝置表登記了乙個字元裝置。register_chrdev需要三個引數,參 數一是希望獲得的裝置號,如果是零的話,系統將選擇乙個沒有被占用的裝置號返回。引數二是裝置檔名,引數三用來登記驅動程式實際執行操作的函式的指標。

如果登記成功,返回裝置的主裝置號,不成功,返回乙個負值。

void cleanup_module(void)

在用rmmod解除安裝模組時,cleanup_module函式被呼叫,它釋放字元裝置test在系統字元裝置表中占有的表項。

乙個極其簡單的字元裝置可以說寫好了,檔名就叫test.c吧。

下面編譯 :

$ gcc -o2 -dmodule -d__kernel__ -c test.c –c表示輸出制定名,自動生成.o檔案

得到檔案test.o就是乙個裝置驅動程式。

如果裝置驅動程式有多個檔案,把每個檔案按上面的命令列編譯,然後

ld ?-r ?file1.o ?file2.o ?-o ?modulename。

驅動程式已經編譯好了,現在把它安裝到系統中去。

$ insmod ?–f ?test.o

如果安裝成功,在/proc/devices檔案中就可以看到裝置test,並可以看到它的主裝置號。要解除安裝的話,執行 :

$ rmmod test

下一步要建立裝置檔案。

mknod /dev/test c major minor

c 是指字元裝置,major是主裝置號,就是在/proc/devices裡看到的。

用shell命令

$ cat /proc/devices

就可以獲得主裝置號,可以把上面的命令列加入你的shell script中去。

minor是從裝置號,設定成0就可以了。

我們現在可以通過裝置檔案來訪問我們的驅動程式。寫乙個小小的測試程式。

#include

#include

#include

#include

main()

read(testdev,buf,10);

for (i = 0; i < 10;i++)

printf("%d/n",buf[i]);

close(testdev);

} 編譯執行,看看是不是列印出全1 ?

linux裝置驅動程式 字元裝置驅動程式

先留個 有一起學習驅動程式的加qq295699450 字元裝置驅動 這篇比較惱火。載入成功,但是讀不出來資料,有知道怎麼回事的,留個言,一起討論下 資料結構 struct scull mem struct scull dev dev 整個驅動程式 如下 include include include...

Linux裝置驅動程式 字元裝置驅動程式

1.檢視主裝置號,次裝置號 進入 dev目錄執行ls l,第四,五列分別為主次裝置號,10,180,1,5,這些是主裝置號,而60,63這些就是次裝置號 130 shell android dev ls l crw rw r system radio 10,60 1969 12 31 21 00 a...

Linux裝置驅動程式 字元裝置驅動程式設計

linux對裝置的操作與對檔案的操作是一樣的,可以看到乙個裝置所對應的檔案。我們平時用的read write等函式也可以用於裝置檔案。字元裝置 以字元為單位 按照順利操作 沒有快取區,故不支援隨機讀寫 例外 幀快取裝置,如顯示卡,是可以隨機訪問的 裝置號由主裝置號與次裝置號組成。主裝置號標識裝置對應...