第二章 構造和執行模組(筆記)

2021-06-06 13:34:56 字數 4141 閱讀 4954

如果讀者正在編寫乙個只適用於某特定發行版的驅動程式,則應該針對相關核心建立和測試自己的驅動程式。

2.6.x核心構造模組,必須在自己的系統中配置並構造好核心樹(因為2.6核心的模組要和核心源**樹中的目標檔案連線)。先前的核心只需要一套核心標頭檔案就夠了。

/** module_license用來告訴核心,該模組採用自由許可證;

* moudule.h包含有可裝載模組需要的大量符號和函式定義。

* init.h包含初始化和清楚函式。

*/#include

#include

module_license("dual bsd/gpl");

static int hello_init(void)

static void hello_exit(void) /*

* moudule_init和module_exit行使用了核心的特殊巨集來表示上述兩個函式所扮演的角色。

*/module_init(hello_init);

module_exit(hello_exit);

a. 大多數小規模及中規模應用程式是從頭到尾執行單個任務,而模組卻只是預先註冊自己以便服務於將來的某個請求,然後它的初始化函式就立即結束(事件驅動程式的應用程式和核心**之間的另乙個主要不同是:應用程式在退出時,可以不管資源的釋放和其他的清除工作,但模組的退出函式卻必須仔細撤銷初始化函式所做的一切,否則,在系統重新引導之前某些東西就會殘留在系統中)。

b. 應用程式可以呼叫它並未定義的函式,這是因為連線過程能夠解析外部引用從而使用適當的函式庫;而模組僅僅被鏈結到核心,因此它能呼叫的函式僅僅是由核心匯出的那些函式,而不存在任何可鏈結的函式庫。

c. 核心程式設計和應用程式程式設計的另外一點重要不同之處在於各環境下處理錯誤的方式不同:應用程式開發過程中的段錯誤是無害的,並且總是可以使用偵錯程式跟蹤到源**的問題所在,而乙個核心錯誤即使不影響整個系統,也至少會殺死當前程序。

模組執行在所謂的核心空間,而應用程式執行在所謂的使用者空間中。

作業系統的作用是為應用程式提供乙個對計算機硬體的一致檢視。

在unix當中,核心執行在最高端別(也稱作超級使用者態),這個級別中可以進行所有的操作。而應用程式執行在最低級別(及所謂的使用者態)。

有幾方面的原因促使核心程式設計必須考慮併發問題。首先,linux系統中通常正在執行多個併發程序,並且可能有多個程序同時使用我們的驅動程式。其次,大多數裝置能夠中斷處理器,而中斷處理程式非同步執行,而且可能在驅動程式正試圖處理其他任務時被呼叫。還有,linux還可以執行在對稱多處理器系統上,因此可能同時有不止乙個cpu執行我們的驅動程式。最後,2.6核心**已經是可搶占的,這意味著即使在單處理器系統上也存在許多類似多處理器系統的併發問題。

結果,linux核心**(包括驅動程式**)必須是可重入的,他必須能夠同時執行在多個上下文中。

current指標指向當前正在執行的程序。

應用程式在虛擬記憶體中布局,並且有一塊很大的棧空間。

而相反的是,核心具有非常小的棧,他可能只和乙個4096位元組大小的頁那樣小。如果我們需要大的結構,則應該在呼叫時動態分配該結構。

核心api中具有兩個下劃線字首(_ _)的函式名稱通常是介面的底層元件。

核心**不能實現浮點數運算。

# 如果已定義kernelrelease,則說明是從核心構造系統呼叫的,

# 因此可以利用其內建語句。

ifneq ($(kernelrelease),)

obj-m := hello.o

# 否則,是直接從命令列呼叫,

# 這是要呼叫核心構造系統。

else

kerneldir ?= /lib/modules/$(shell uname -r)/build

pwd := $(shell pwd)

# 首先改變目錄到-c選項指定的位置(即核心源**目錄),

# m=選項讓該makefile在構造modules目標之前返回到模組源**目錄,

# modules目標指向obj-m變數中設定的模組。

default:

$(make) -c $(kerneldir) m=$(pwd) modules

endif

insmod將模組的**和資料裝入核心,然後使用核心的符號表解析模組中任何未解析的符號(與聯結器不同,核心不會修改模組的磁碟檔案,而僅僅修改記憶體中的副本)。

核心如何支援insmod工作的?實際上它依賴於定義在kernel/module.c中系統呼叫。函式sys_init_module給模組分配核心記憶體(函式vmalloc負責分配記憶體)以便裝載模組,然後,該系統呼叫將模組正文複製到記憶體區域,並通過核心符號解析表解析模組中的核心引用,最後呼叫模組的初始化函式。

modprobe也用來將模組裝載到核心中。它和insmod的區別在於,他會考慮要裝載的模組是否引用了一些當前核心不存在的符號。

我們可以用rmmod工具從核心中移除模組。

公共核心符號表中包含了所有的全域性核心項(即函式和變數)的位址,這是實現模組化驅動程式所必須的。當模組被裝入核心後,它所匯出的任何符號都會變成核心符號表的一部分。

模組層疊技術(並口驅動程式的模組層疊技術)

模組層疊技術在複雜的專案中非常有用。新模組可以使用由我們自己的模組匯出的符號,這樣我們可以在其他模組上層疊新的模組。

linux核心標頭檔案提供了乙個方便的方法來管理符號對模組外部的可見性,從而減少了可能造成的名字空間汙染(名字空間的名稱可能會和核心其他地方定義的名稱發生衝突),並且適當隱藏資訊。如果乙個模組需要向其他模組匯出符號,則應該使用下面的巨集。

export_symbol(name);

export_symbol_gpl(name);

__gpl版本使得匯出的模組只能被gpl許可證下的模組使用。符號必須在模組檔案的全域性部分匯出,不能在函式中匯出,這是因為上面這兩個巨集將被擴充套件為乙個特殊變數的宣告,而該變數必須是全域性的。改變量將在模組可執行檔案的特殊部分(即乙個「elf」段)中儲存,在裝載時,核心通過這個段來找模組匯出的變數。    

module_license

module_author

module_description

module_version

module_alias

module_device_table

初始化函式應該被宣告為static,因為這種函式在特定檔案之外沒有其他意義。

_ _init標記對核心來講是一種暗示,表明該函式僅在初始化期間使用。

module_init的使用是強制性的。這個巨集會在模組的目標**中增加乙個特殊的段,用以說明核心初始化函式所在的位置沒有個定義初始化函式永遠不會被呼叫。

能夠註冊的設施型別包括串列埠、雜項裝置、sysfs入口、/proc檔案、可執行域以及線路規程(line discipline)等。很多可註冊的設施所支援的功能屬於」軟體抽象「範疇,而不與任何硬體直接相關。

_ _exit修飾詞標記該**僅用於模組解除安裝(編譯器將把該函式放在特殊的elf段中)。module_exit宣告對於幫助核心找到該模組的清楚函式是必須的。

如果在發生了某個特定型別的錯誤後無法繼續裝載模組,則要將出錯之前的任何工作撤銷。如果由於某種原因我們未能撤銷已註冊的設施,則該核心會處於一種不穩定狀態,這是因為核心包含了一些指向並不存在的**的內部指標。

處理錯誤時我們可以使用goto語句。

在首次註冊完之後,**就應該準備好被核心的其他部分呼叫;在用來支援某個設施的所有內部初始化完成之前,不要註冊任何設施。

當初始化失敗而核心的某些部分已經使用了模組所註冊的某個設施時,則應該仔細處理核心其他部分正在進行的操作,並且要等待這些操作的完成。

可以使用下面的命令列來裝載該模組:

insmod hellop howmany=10 whom=」mom「

static char* whom = 「world」;

static int howmany = 1;

module_param(howmany, int, s_irugo);

module_param(whom, charp, s_irugo);

要生命陣列引數,需要使用下面的巨集:

module_param_array(name, type, num, perm);

name是陣列的名稱,type是陣列元素的型別,num是乙個整數變數,而perm是常見的訪問許可值。

核心支援的模組引數型別如下:bool、invbool、charp、int、long、short、uint、ulong、ushort

使用者空間編寫驅動程式的有點和缺點歸納:

Linux 驅動 第二章 構造和執行模組

設定測試系統開發環境及hello world入門模組在前面博文中已經講到,請參考 一,核心模組與應用程式的對比 應用程式 小規模及中規模程式,從頭到尾執行單個任務。核心模組 預先註冊自己,以便服務於將來的某個請求。然後他的初始化函式就立即結束。退出時候,應用程式可以不釋放自己申請的資源,而模組在退出...

第二章 建構函式

default constructor的構造操作 什麼時候才會合成乙個default construct 當編譯器需要它的時候,此外,被合成出來的 construct 只執行編譯器所需要的行動。nontivial default constructor的 4種情況 1.帶有 default cons...

第二章筆記

1.遞迴查詢和迭代查詢 遞迴查詢是一條環路,直接想成遞迴的定義就行,你想查乙個 的ip,首先將這個 傳給它的本地dns,然後認為本地dns可以直接將ip給你,然後本地dns為了知道這個ip又詢問下個dns伺服器。從此可以看出,增加了被涉及的伺服器的資料,所以一般用迭代查詢,迭代查詢是你詢問完後給你下...