字元驅動程式例項剖析
我們來寫乙個最簡單的字元裝置驅動程式。雖然它什麼也不做,但是通過它可以了解linux的裝置驅動程式的工作原理。把下面的c**輸入機器,你就會獲得乙個真正的裝置驅動程式。
由於使用者程序是通過裝置檔案同硬體打交道,對裝置檔案的操作方式不外乎就是一些系統呼叫,如 open,read,write,close…, 注意,不是fopen, fread,但是如何把系統呼叫和驅動程式關聯起來呢?這需要了解乙個非常關鍵的資料結構:
struct file_operations {
int (*seek) (struct inode * ,struct file *, off_t ,int);
int (*read) (struct inode * ,struct file *, char ,int);
int (*write) (struct inode * ,struct file *, off_t ,int);
int (*readdir) (struct inode * ,struct file *, struct dirent * ,int);
int (*select) (struct inode * ,struct file *, int ,select_table *);
int (*ioctl) (struct inode * ,struct file *, unsined int ,unsigned long);
int (*mmap) (struct inode * ,struct file *, struct vm_area_struct *);
int (*open) (struct inode * ,struct file *);
int (*release) (struct inode * ,struct file *);
int (*fsync) (struct inode * ,struct file *);
int (*fasync) (struct inode * ,struct file *,int);
int (*check_media_change) (struct inode * ,struct file *);
int (*revalidate) (dev_t dev);
這個結構的每乙個成員的名字都對應著乙個系統呼叫。使用者程序利用系統呼叫在對裝置檔案進行諸如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)
int left; 使用者空間和核心空間
if (verify_area(verify_write,buf,count) == -efault )
return -efault;
for(left = count ;left >0 ;left--)
__put_user(1,buf,1);
buf++;
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)
return count;
static int open_test(struct inode *inode,struct file *file )
mod_inc_use_count; 模組計數加以,表示當前核心有個裝置載入核心當中去
return 0;
static void release_test(struct inode *inode,struct file *file )
mod_dec_use_count;
這幾個函式都是空操作。實際呼叫發生時什麼也不做,他們僅僅為下面的結構提供函式指標。
struct file_operations test_fops = {?
read_test,
write_test,
open_test,
release_test,
裝置驅動程式的主體可以說是寫好了。現在要把驅動程式嵌入核心。驅動程式可以按照兩種方式編譯。一種是編譯進kernel,另一種是編譯成模組(modules),如果編譯進核心的話,會增加核心的大小,還要改動核心的原始檔,而且不能動態的解除安裝,不利於除錯,所以推薦使用模組方式。
int init_module(void)
int result;
result = register_chrdev(0, "test", &test_fops); 對裝置操作的整個介面
if (result <0) {
printk(kern_info "test: can't get major number ");
return result;
if (test_major == 0) test_major = result; /* dynamic */
return 0;
在用insmod命令將編譯好的模組調入記憶體時,init_module 函式被呼叫。在這裡,init_module只做了一件事,就是向系統的字元裝置表登記了乙個字元裝置。register_chrdev需要三個引數,引數一是希望獲得的裝置號,如果是零的話,系統將選擇乙個沒有被占用的裝置號返回。引數二是裝置檔名,引數三用來登記驅動程式實際執行操作的函式的指標。
如果登記成功,返回裝置的主裝置號,不成功,返回乙個負值。
void cleanup_module(void)
unregister_chrdev(test_major,"test");
在用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()
int testdev;
int i;
char buf[10];
testdev = open("/dev/test",o_rdwr);
if ( testdev == -1 )
printf("cann't open file ");
exit(0);
read(testdev,buf,10);
for (i = 0; i <10;i++)
printf("%d ",buf[i]);
close(testdev);
編譯執行,看看是不是列印出全1 ?
字元裝置驅動程式例項一
字元裝置驅動 demo.c 動態註冊裝置號 自動建立裝置檔案 include include include include include define count 3 define baseminor 0 define name demo dev struct cdev cdevp null d...
linux 驅動程式 高階字元驅動程式
ioctl方法 驅動程式的原型實現 int ioctl struct inode inode,struct file filp,unsigned int cmd,unsigned long arg ioctl 命令選擇 位段結構 number direction ioc read ioc write...
linux裝置驅動程式 字元裝置驅動程式
先留個 有一起學習驅動程式的加qq295699450 字元裝置驅動 這篇比較惱火。載入成功,但是讀不出來資料,有知道怎麼回事的,留個言,一起討論下 資料結構 struct scull mem struct scull dev dev 整個驅動程式 如下 include include include...