linux驅動(網摘)

2021-05-24 02:24:08 字數 3954 閱讀 7448

分類:

linux

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 //所有模組都需要的標頭檔案

#include

#include

#include // 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");  

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

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

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

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

Linux路由配置網摘

pc1 rout1 rout2 rout3 pc2 五個裝置的靜態ip位址分別為 pc1 192.168.1.88 24 rout1 192.168.1.128 24 192.168.2.128 24 rout2 192.168.2.66 24 192.168.3.66 24 rout3 192.1...

網摘精靈教程 網摘精靈延時功能詳解

網摘精靈教程 網摘精靈延時功能詳解 網摘精靈軟體裡面有個功能設定項是 延時 這個延時如何設定呢?簡單地說,就是傳送資料一次,最多花費多久時間。這個是以秒為單位的,設定值應該是數字。我們使用瀏覽器訪問 的一般過程是 輸入 發出請求,解析為伺服器ip位址,伺服器接收請求,進行相應處理,返回響應到瀏覽器,...

月亮網摘(2006 10 11)

類網不易 新奇特科技產品介紹站點,最好玩的地方在於站長會用非常幽默的口吻模擬乙個個場景,然後引出他要介紹的東西。帶我遛遛 gmail申請秘密通道 你是知恩圖報的人嗎?成功註冊了gmail以後不但要報答站長,也要記得報答我啊!語錄中國 嗯,當我們需要寫網路議 的時候,從這裡摘抄幾條語錄是不錯的主意。新...