Linux驅動程式入門 Hello World

2021-05-23 02:04:07 字數 4015 閱讀 2895

2008-7-16 22:06:17    收藏  |  列印  | 投 票(74)

中小 ]

linux驅動程式入門—hello world

ohy 20080716

1、引言

記得在學習vc++和c語言的時候,一開始都會以乙個hello world的例子作為演示,將學者逐漸引入殿堂,這個幾乎成了計算機程式語言學習必經的乙個入門之路。

當然,在學習linux程式設計的時候也是這樣,下面的例子應該是再熟悉不過了:

首先用vi編寫乙個c程式:vi hello.c

#include  "stdio.h"

int main()

接著用gcc進行編譯:gcc -o hello hello.c

最後執行該程式:./hello

在終端上你會看到:hello world!!!

上面的是在作業系統基礎上進行的使用者應用程式的開發。然而對於linux驅動程式的開發是 絕然不同的,因為驅動程式的開發是執行在核心空間的,而應用程式是執行在使用者空間的。雖然hello world是乙個簡單得不能再簡單的程式,但是對於嵌入式linux驅動程式的初學者來說,通過這個過程的操作可以對linux驅動程式開發的過程和其中 的一些概念有乙個深刻的認識。所以,我在這裡也就以前學習linux的基礎上整理了一下,寫了這篇部落格。一方面是自己對這方面知識的回顧和鞏固,另一方面 更是希望這裡的內容能給大家提供那麼一點點有用的資訊,小弟心裡就很高興了。當然希望有高手可以做下評價和指導,及時糾正小弟的錯誤,謝謝先。

2、概念

驅動程式作為系統核心的一部分,它工作在核心態,而應用程式工作在使用者態。也就是說,程式不能直接通過指標,把使用者空間的資料位址傳遞給核心(因為mmu 對映的位址根本不一樣)。要想在應用程式和驅動程式之間傳遞資料(指標),就需要經過轉換。把使用者態「看到」的空間位址轉換成核心態可訪問的位址。 linux系統提供了一系列方便的函式實現這種轉換,如get_user、put_user、copy_from_user、copy_to_user 等,它們自己負責訪問許可權的檢查,使用時,不需要關係更多的問題。

linux核心把驅動程式劃分為3種型別:字元裝置、塊裝置和網路裝置。字元裝置和塊裝置可以像檔案一樣被訪問。它們的主要區別不在於能否seek,而是 在於系統對於這兩種型別裝置的管理方式。應用程式對於字元裝置的每乙個i/o操作,都會直接傳遞給系統核心對應的驅動程式;而應用程式對於塊裝置的操作, 要經過系統的緩衝區管理,間接傳遞給驅動程式處理。塊裝置的這種管理方式是為儲存提供優化的;而字元裝置的管理方式是為操作提供優化的。至於網路裝置,它 在linux系統中是一模擬較特殊的裝置它不像字元裝置或塊裝置那樣通過對應的裝置檔案節點去訪問,核心也不再通過read和write等呼叫去訪問網路 裝置。linux的網路系統主要是基於bsd unix的套接字機制,在系統和驅動程式之間有專門的資料結構進行資料傳輸,系統支援對資料傳送和資料接收快取,提供流量控制機制,提供更多的協議支援。

在linux系統中,驅動程式都做成模組的形式,也就是module。簡單的說,乙個模組提供乙個功能,這些模組是可以按照需要隨時裝入核心空間和從核心 空間解除安裝的。因此,核心模組是為了給核心動態增減功能而設計的,並不僅僅是限於驅動程式。

關於核心模組初始化(載入)函式

當使用者輸入命令「insmod 模組檔名」(或者其他方式)載入核心模組時,系統會檢測此模組能否被載入,如果能被載入,核心呼叫模組的初始化函式。在linux 2.4中,核心模組的初始化函式名為init_module()。但如果驅動程式需要編譯進核心,則初始化函式不能與核心的其他部分(包括其他核心模組) 的函式同名。這樣,如果程式可能編譯到核心中時就比較麻煩。不過在這個標頭檔案中定義了巨集module_init(), 用來遮蔽兩者的差別,事實上,使用這個巨集可以使驅動程式向上相容linux 2.6,而相容linux 2.0也比較方便。

關於核心模 塊清除(解除安裝)函式

當使用者輸入命令「rmmod 模組檔名」(或者其他方式)解除安裝核心模組時,此時,系統會檢測此模組是否能被解除安裝,核心將呼叫模組清除函式。在linux 2.4中,清除函式的函式名稱為cleanup_module()。但如果驅動程式需要編譯進核心,則初始化函式不能與核心的其他部分(包括其他核心模 塊)的函式同名。這樣,如果程式可能編譯到核心中時就比較麻煩。不過在這個標頭檔案中定義了巨集 module_exit(),用來遮蔽兩者的差別。  

3、例項

因為核心模組需要載入到核心空間,所以其程式的編寫與一般應用程式不同,在裡面再也找不到類似main()這樣的入口函式,下面對應函式相應的源** hello.c,介紹乙個驅動模組的寫法。(由於尖括號不能在部落格上顯示,固用雙括號代替,在程式設計除錯的時候換回來就可以了)。

#ifndef __kernel__

#define __kernel__

#endif

#ifndef module

#define module

#endif

#include 《linux/module.h》  //所有模組都需要的標頭檔案

#include 《linux/sched.h》 

#include 《linux/kernel.h》

#include 《linux/init.h》        // init和exit相關巨集

module_license("gpl");

int text_init(void)

void text_cleanup(void)

module_init(text_init);             //註冊載入時執行的函式

module_exit(text_cleanup);     //註冊解除安裝時執行的函式

4、除錯

乙個linux核心模組需包含模組初始化和模組解除安裝函式,前者在insmod的時候執行,後者在rmmod的時候執行。初始化與解除安裝函式必須在巨集 module_init和module_exit使用前定義,否則會出現編譯錯誤。

程式中的module_license("gpl")用於宣告模組的許可證。

編譯:gcc -c -i /usr/src/linux-2.4/include/ hello.c

執行:insmod hello.o

終端上會顯示:

localhost kernel:hello world!

同時在/proc/modules裡面會看到相應的裝置資訊:

more /proc/modules

你會看到(或許後面的數值不一樣):

hello           844   0 (unused)

.......

解除安裝驅動程式:

rmmod hello

你將會在終端上面看到:

localhost kernel:goodbye world!  

5、注意:

<1> 在gcc編譯選項中增加-c

<2>在gcc編譯選項中定義兩個巨集:-dmodule -d__kerenl__

或直接在原始檔中定義這兩個巨集:

#define module

#define __kernel__

<3> 在原始檔中包括module.h檔案:

#include  "linux/module.h"

<4> 假定你現在執行的核心的原始碼目錄絕對路徑是mykernelsrcpath,在gcc編譯時增加選項:

-i $mykernelsrcpath/include (如-i /usr/src/linux/include)

<5>某些時候用 insmod -f能夠成功載入,但需謹慎使用。

<6>如果看不到用printk列印的資訊,可以用dmesg命令看。

<7> 列印訊息受級別的限制,訊息級別可以通過printk設定,如:

printk(" 《n》something");   /* 其中0<=n<=7 */

假設控制台的訊息級別為m, 當n

這樣一方面可以提高要列印訊息本身的級別(數字越小級別越高),

另一方面可以改變控制台的訊息級別(可從1到8),如改為8可用以下命令:

# echo "8" > /proc/sys/kernel/printk

Linux 驅動程式入門

驅動是具有入口和出口的一組方法的集合,這一組方法才是驅動的核心內容。對於字元裝置驅動程式,最核心的就是 file operation 結構,這個結構實際上是提供給虛擬檔案系統 vfs 的檔案介面,它的每乙個成員函式一般都對應乙個系統呼叫。使用者程序利用系統呼叫對裝置檔案進行諸如讀和寫操作時,系統呼叫...

Linux驅動程式入門 Hello World

1 引言 記得在學習vc 和c語言的時候,一開始都會以乙個hello world的例子作為演示,將學者逐漸引入殿堂,這個幾乎成了計算機程式語言學習必經的乙個入門之路。當然,在學習linux程式設計的時候也是這樣,下面的例子應該是再熟悉不過了 首先用vi編寫乙個c程式 vi hello.c inclu...

linux 驅動程式 高階字元驅動程式

ioctl方法 驅動程式的原型實現 int ioctl struct inode inode,struct file filp,unsigned int cmd,unsigned long arg ioctl 命令選擇 位段結構 number direction ioc read ioc write...