簡單linux驅動小例子

2021-06-07 09:04:26 字數 3193 閱讀 9655

我們來寫乙個最簡單的字元裝置驅動程式。雖然它什麼也不做,但是通過它可以了解linux的裝置驅動程式的工作原理。把下面的c**輸入機器,你就會獲得乙個真正的裝置驅動程式。不過我的kernel是2。0。34,在低版本的kernel上可能會出現問題,我還沒測試過。

#define __no_version__

#include

#include

char kernel_version = uts_release;

這一段定義了一些版本資訊,雖然用處不是很大,但也必不可少。johnsonm說所有的驅動程式的開頭都要包含,但我看倒是未必。由於使用者程序是通過裝置檔案同硬體打交道,對裝置檔案的操作方式不外乎就是一些系統呼叫,如 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 *node,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。

static int write_tibet(struct inode *inode,struct file *file,

const char *buf,int count)

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

static void release_tibet(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

得到檔案test。o就是乙個裝置驅動程式。如果裝置驅動程式有多個檔案,把每個檔案按上面的命令列編譯,然後:

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

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

$ insmod -f test

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

$ rmmod test

下一步要建立裝置檔案。

mknod /dev/test c major minor

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

$ cat /proc/devices | awk "\$2=="test" "

就可以獲得主裝置號,可以把上面的命令列加入你的shell script中去。minor是從裝置號,設定成0就可以了。我們現在可以通過裝置檔案來訪問我們的驅動程式。寫乙個小小的測試程式。

#include 

#include

#include

#include

main()

read(testdev,buf,10);

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

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

close(testdev);

}

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

以上只是乙個簡單的演示。真正實用的驅動程式要複雜的多,要處理如中斷,dma,i/o port等問題。這些才是真正的難點。請看下節,實際情況的處理。

c 反射 簡單小例子

介面using system usingsystem.text namespacejiang.iform voidrun 以下是實現介面的類 using system using system.drawing using system.text using system.windows.forms ...

webSql的簡單小例子

初始化websql資料庫的引數資訊 1 var config 7 var db window.opendatabase config.name,config.version,config.desc,config.size 此函式為建立乙個表 1 function cratetable errorca...

簡單的Linux驅動

helloworldkernel 核心程式結構分析 前提是已經編譯配置好核心 現在,通過乙個最簡單的helloworld程式來將核心基礎做一下分析 因為核心在載入之後就一直執行在記憶體中,為了保證linux核心 的小而核心,使用模組的方式來開發驅動。模組就是在硬碟上的一段程式,通過核心特定的操作可以...