module的相關的巨集定義都在include/linux/module.h中。
module_init,module_exit也是巨集,在init.h中定義。
init和exit函式指標的宣告如下:
typedef
int (*initcall_t)(void); //注意,返回0才是success,負數指明錯誤號。
typedef
void (*exitcall_t)(void);
模組執行在核心空間,而應用程式執行在使用者空間。
這兩個概念是作業系統執行的理論基礎之一。作業系統需要保護資源不被非法訪問,在cpu能夠保護系統不被應用軟體破壞的時候才能完成。一般通過cpu的級別來控制訪問的許可權,目前的cpu一般都具有2個以上的保護級別,unix只使用其中的兩種,最高和最低的兩個來實現使用者空間和核心空間。
核心空間和使用者空間,除了有不同的優先權等級,也都有自己的記憶體對映。
當執行系統呼叫或者發生硬體中斷的時候,unix將執行模式從使用者空間切換到核心空間。
系統呼叫的時候,核心**執行在程序的上下文中,能夠訪問到程序的位址空間。
而中斷是和使用者程序非同步的,和程序是沒有關係的。
模組是在核心空間中執行來提供功能。
核心程式設計要記住,任意時候都可能有很多事情在同時發生。
有幾個原因要求我們注意併發的問題:
核心中通過current變數來訪問當前程序,它是指向struct task_struct的指標,通過current->comm可以訪問到當前程序的程式名,current->pid可以找到當前程序的程序號。
current在早期是乙個全域性的變數,在
首先要確保編譯工具的版本的正確性,在documention/changes中可以查到。
其次是編寫module的makefile。
obj-m : [moduel_name].o
[module_name]-objs := file1.o file2.o
default:
make -c [kernel_folder] m=$(shell pwd) modules
obj-m是指定module的object名字;
[module_name]-objs是宣告了module object要如何生成;
-c可以在make file中看到,是kernel的root目錄;
ps:需要將對應核心先配置再編譯一次,才能編譯模組。如果核心的輸出被重新指定,那麼模組的make也要加上-o引數來指定編譯目錄。insmod和ld有些類似,將模組的**和資料load到記憶體中去,然後用核心的符號表解析模組中未解析的符號。和ld不同的是,insmod只是修改了記憶體中的**,不會修改磁碟上的符號。
insmod可以支援接受命令行的引數,並將這些引數傳遞給具體的模組。這樣,模組在不同的情況下由使用者來進行配置,有更好的靈活性。
modprobe也是用來裝載模組,和insmod不用地方在於,它在裝載時候會搜尋其他依賴的模組,並將其也裝載進來。
對於insmod來說,如果依賴的module沒有裝載,會報」resolvedsymblos」的錯誤。
lsmod列出已經insmod好的模組,lsmod是通過讀取/proc/modules來獲取模組資訊的。
乙個小規則, 「sys_」開頭的函式,只有系統呼叫函式才是這樣的。modversins,是由config_modversions定義的,他會對內建的一些函式的symbol加上特定的版本資訊,這樣的情況下,如果module也同樣使用modversions,則需要嚴格的保證兩者在同樣的版本下。
核心不會假定乙個給定的模組是針對正確的核心版本構造的,所以我們需要方法來幫助核心來檢測模組的相容性:
如果需要module實現能夠支援多個版本的linux相容,因為核心開發介面的變化,則模組需要使用巨集來區分不同的linux版本。
linux在linux/version.h中定義相關的巨集,包含以下常用的巨集:
uts_release:給出核心版本的字串,如」2.6.10」。
linux_version_code:版本號的二進位制表示。如版本2.6.10,整型數為132618,二進位制位0x02060a,每8個bit為對應乙個版本資訊,這也意味著每個版本號的區間只有0~255.
kernel_version(major,minor, release):從版本號建立出整型數的版本號平也就是和linux_version_code一樣的形式。
主要是考慮到不同的處理器情況下如何正確的工作,這裡我們一樣使用vermagic.o來讓核心幫助我們檢測我們的模組針對處理器的編譯是否和核心一致。
模組可以向核心匯出一些符號,來方便別的模組使用,這樣我們可以基於這個模組夠構建出更上層的模組,比如說usb輸入裝置就是構造在usbcore和input模組之上的。
linux核心提供了一組巨集來管理符號對模組外部的可見性來減少名空間的汙染,並且可以適當的隱藏一些資訊。
export_symbol(name);
export_symbol_gpl(name);
帶」_gpl」的只能被gpl模組所使用,這兩個巨集要在全域性部分宣告,在函式內部宣告是無效的。
export出去的symbol會被儲存在module可執行檔案乙個特定的section中,在核心載入模組的時候,通過這個段來尋找模組匯出的變數。
module的初始化函式一般如下:
static
int __init initialization_function(void)
module_init(initialization_function);
static是必須的,因為init只對本module有效。
__init對於最終object沒有實際的意義,它是告訴核心,這個函式僅僅在初始化的時候使用,在用完之後,核心裝載器可以把這個函式占用的記憶體釋放出去了。
__init_data和__init類似,修飾的是變數而已。
當然在核心沒有被配置成支援熱插拔的情況下才會如此這兩個宣告。
module_init實際是在ko中生成了乙個特殊的段(或者說是乙個symbol?),讓核心能夠找到初始化函式。
和init類似,每個模組都要註冊乙個exit函式來清除模組所使用的資源,一般如下:
static
void __exit cleanup_function(void)
module_exit(cleanup_function);
__exit用來修飾模組的exit函式,如果該模組直接內嵌於核心或者核心不允許解除安裝模組,則這個__exit修飾的函式會直接被丟棄。
exit函式對於模組不是必須的,可以不定義,不定義則意味著模組不能被解除安裝。
模組**在初始化過程中必須始終檢查所有操作返回值確保所有的操作都已經真正的成功了。
如果初始化的時候出現了錯誤,首先判斷模組是否還能繼續初始化,否則只能進行出錯處理來退出。
通常建議模組在出錯的時候,可以降低需求和提供的功能來讓模組可以工作起來。
出錯處理中,需要將所有已經申請的資源都釋放掉,否則系統會處於乙個不穩定狀態。
出錯處理,比較常見的是使用goto語句,來統一進行資源的釋放。
另外一種方式是記錄下申請的資源,然後呼叫exit函式來清除已經申請的資源(注意不能宣告__exit)。
出錯返回的錯誤編碼在linuxt/errno.h中定義的負整數。
競態(race conditon),對於我們編寫模組的時候要考慮下面的兩個點:
模組的引數使用module_param巨集來宣告,巨集在moduleparam.h中定義。
module_param定義需要3個引數,變數名字,變數型別和用於sysfs入口的訪問掩碼,宣告必須要放在全域性部分,一般放在source的頭部。一般如下:
staticchar *whom = "world";
module_param
(whom, charp, s_irugo)
核心支援模組引數的型別有:bool, invbool, charp, int ,long ,short, uint, ulong, ushort。如果我們需要的型別不在其中,也可以自定義型別。
模組也支援陣列引數,一般如下:
int numlist[10]=;
module_param_array(numlist, int, 100, irugo);
所有的模組引數都需要有乙個預設值,來確保可以不帶引數裝載模組也能正常工作。
模組引數一般會在/sys/modules/[module_name]/parameters/中生成對應的sysfs檔案,如果宣告的perm為0,則不會有這個sysfs入口。
如果模組引數通過sysfs被修改,核心不會對模組進行通知,需要模組自己檢測到,並作出相應的變化。
模組引數的許可權,不能將寫許可權放給other使用者,編譯系統會報「negative width in bit-field 『』」的錯誤。使用者控制項編寫驅動程式相對容易,在某些情況下是替代核心空間驅動程式的乙個好方法。
使用者空間驅動程式有如下優點:
一般,使用者空間的驅動程式實現為乙個服務程序,它會代替核心作為硬體控制的唯一**,客戶應用程式連線到服務程序來和實際裝置互動。
使用者驅動程式一樣有其缺點:
android的hal和對應的service實際就是構成乙個使用者空間的驅動程式,這樣就規避核心的gpl許可問題,廠商可以直接給出hal的so來驅動硬體。
構造和執行模組
include include module license dual bsd gpl 如果沒有這行,編譯器會產生抱怨 static int hello init void static void hello exit void module init hello init 指定裝載模組時初始化函式...
構造和執行模組
1.可在執行時新增到核心中的 被稱為 模組 ldd3 p13 可裝載模組 2.在root許可權下 使用insmod module name.ko裝載模組 使用rmmod module name解除安裝模組 3.hello world模組 4.makefile 5.如果使用虛擬機器,在控制台看不到pr...
構造和執行模組
構造乙個模組並不難,難的是如何把自己驅動的元件理解好,並最大化其效能,說的俗一點就是怎麼利用好器件的功能。核心模組程式和應用程式 大多數的小規模應用程式都是從頭到尾執行單個任務 而模組卻只是先註冊自己 以便服務以將來,接著它的初始化函式就立即結束。模組化程式執行在核心空間,是核心功能的乙個擴充套件,...