Linux內核模塊的編譯基礎知識

關於linux內核驅動的東西網絡上有很多,但網上的東西還是感覺有點籠統,讀過之後就忘了,還是需要寫下來,或者寫到本子上,自己形成一個概念好一些。讀了這本書上的東西,把覺得好的東西寫下來,已備不時之用,也強化記憶。
1 內核模塊的概念
介紹內核模塊的同時,也說明一下和應用程序的區別。雖然內容很多,但覺得都很有用。
1、內核模塊是一些可以讓操作系統內核在需要時載入和執行的代碼,同時在不需要的時候可以卸載。這是一個好的功能,擴展了操作系統的內核功能,卻不需要重新啓動系統,是一種動態加載的技術。
特點:動態加載,隨時載入,隨時卸載,擴展功能
2、內核模塊的加載作用:只是向linux內核預先註冊自己,以便於將來的請求使用。
也就是告訴內核,它有了新增的功能,而並不馬上使用(執行),而應用程序在加載後就開始執行。
3、內核模塊的代碼編寫沒有外部的函數庫可以用,只能使用內核導出的函數。而應用程序習慣於使用外部的庫函數,在編譯時將程序與庫函數鏈接在一起。例如對比printf( ) and printk( )
4、內核模塊代碼運行在內核空間,而應用程序在用戶空間。應用程序的運行會形成新的進程,而內核模塊一般不會。每當應用程序執行系統調用時,linux執行模式從用戶空間切換到內核空間。
2linux內核模塊的框架
最少兩個入口點
*模塊加載函數
*模塊卸載函數
通常使用module_init() and module_exit()兩個宏定義聲明模塊的加載函數和卸載函數,這個定義在linux2.6.x/include/linux/init.h中。內容爲:
#define module_init(x) __initcall(x)
//在內核啓動或模塊加載時執行
#define module_exit(x) __exitcall(x)
//在模塊卸載時執行
*每一個模塊只能有一個module_init 和一個module_exit。
一個簡單的內核模塊的源代碼.c格式如下:
-------------------------------------------------------------------------------------------
#include<linux/module.h>   //所有內核模塊都必須包含這個頭文件
#include<linux/kernel.h>    //使用內核信息優先級時要包含這個
#include<linux/init.h>         //一些初始化的函數如module_init()
MODULE_LICENSE("GPL");  //模塊許可聲明
static int hello_init(void)
{}
static void hello_exit(void)
{}
module_init(hello_init);
module_exit(hell0_exit);
這兩個函數只是一種內部的聲明,或者說內部實現加載的一種方式,說到低還是在模塊內部的,內核並沒有加載模塊,真正要加載模塊要使用命令insmod卸載用rmmod.
關於linux設備驅動入門請參考另外一篇文章:linux驅動編程入門
--------------------------------------------------------------------------------
3    2.6 系列內核模塊的編譯和加載
這裏講到內核模塊的編譯,2。6版本引入了kbuild,將外部內核模塊的編譯和內核源碼樹的編譯統一起來了。
內核源碼樹的目錄下都有兩個文件Kconfig(2.4版本是Config.in)和Makefile。分佈到各目錄的Kconfig構成了一個分佈式的內核配置數據庫,每個Kconfig分別描述了所屬目錄源文件相關的內核配置菜單。在內核配置make menuconfig(或xconfig等)時,從Kconfig中讀出菜單,用戶選擇後保存到.config的內核配置文件中。在內核編譯時,主 Makefile調用這個.config,就知道了用戶的選擇。
*上面的內容說明了,Kconfig就是對應着內核的配置菜單。如果要想添加新的驅動到內核的源碼中,可以修改Kconfig,這樣就可以選擇這個驅動,如果想使這個驅動被編譯,要修改Makefile
添加新的驅動時需要修改的文件有兩種(注意不只是兩個)
*Kconfig
*Makefile
要想知道怎麼修改這兩種文件,就要知道兩種文件的語法結構
3.1 Kconfig
每個菜單都有一個關鍵字標識,最常見的就是config
語法:
config
symbol是一個新的標記的菜單項,options是在這個新的菜單項下的屬性和選項
其中options部分有:
1、類型定義:
每個config菜單項都要有類型定義,bool布爾類型、 tristate三態:內建、模塊、移除 string字符串、 hex十六進制、 integer整型
例如config HELLO_MODULE
bool "hello test module"
bool類型的只能選中或不選中,tristate類型的菜單項多了編譯成內核模塊的選項,如果選擇編譯成內核模塊,則會在.config中生成一個CONFIG_HELLO_MODULE=m的配置,如果選擇內建,就是直接編譯成內核影響,就會在.config中生成一個 CONFIG_HELLO_MODULE=y的配置.
2、依賴型定義depends on或requires
指此菜單的出現與否依賴於另一個定義
config HELLO_MODULE
bool "hello test module"
depends on ARCH_PXA
這個例子表明HELLO_MODULE這個菜單項只對XScale處理器有效。
3、幫助性定義
只是增加幫助用關鍵字help或者---help---
3.2 內核的Makefile


在linux2.6.x/Documentation/kbuild目錄下有詳細的介紹有關kernel makefile的知識。
linux2.6內核的Makefile分爲5個組成部分:

Makefile     最頂層的Makefile 
.config        內核的當前配置文件,編譯時成爲定層Makefile的一部分
arch/$(ARCH)/Makefile    與體系結構相關的Makefile
s/ Makefile.*      一些Makefile的通用規則
kbuild Makefile           各級目錄下的大概約500個文件,編譯時根據上層Makefile傳下來的宏定義和其他編譯規則,將源代碼編譯成模塊或者編入內核
頂層的Makefile文件讀取 .config文件的內容,並總體上負責build內核和模塊。Arch Makefile則提供補充體系結構相關的信息。 s目錄下的Makefile文件包含了所有用來根據kbuild Makefile 構建內核所需的定義和規則。
(其中.config的內容是在make menuconfig的時候,通過Kconfig文件配置的結果,上面已經說過)
對於大部分內核模塊或設備驅動的開發者和使用者來說,最常接觸到的就是各層目錄下基於kbuild架構的kbuild Makefile文件。主要部分有:
1、目標定義,目標定義就是用來定義哪些內容要做爲模塊編譯,哪些要編譯鏈接進內核。
最簡單的只有一行,如
obj-y += foo.o
表示要由foo.c或者foo.s文件編譯得到foo.o並鏈接進內核,而obj-m則表示該文件要作爲模塊編譯。除了y,m以外的obj-x形式的目標都不會被編譯。
由於既可以編譯成模塊,也可以編譯進內核,更常見的做法是根據.config文件的CONFIG_ 變量來決定文件的編譯方式,如:
obj-$(CONFIG_HELLO_MODULE) += hello.o ,這個已經在7.2.3.1中說明過。
除了obj-形式的目標以外,還有lib-y library庫,hostprogs-y 主機程序等目標,但是基本都應用在特定的目錄和場合下
2、多目標
一個內核模塊由多個源文件編譯而成,這是Makefile有所不同。
採用模塊名加 –objs後綴或者 –y後綴的形式來定義模塊的組成文件。
如以下例子:
obj-$(CONFIG_EXT2_FS) += ext2.o
ext2-y := balloc.o bitmap.o
ext2-$(CONFIG_EXT2_FS_XATTR) += xattr.o
模 塊的名字爲ext2,由balloc.o和bitmap.o兩個目標文件最終鏈接生成ext2.o 直至ext2.ko文件,是否包括xattr.o取決於內核配置文件的配置情況。如果CONFIG_EXT2_FS的值是y也沒有關係,在此過程中生成的 ext2.o將被鏈接進built-in.o最終鏈接進內核。這裏需要注意的一點是,該kbuild Makefile所在的目錄中不應該再包含和模塊名相同的源文件如ext2.c/ext2.s
或者寫成如-objs的形式:
obj-$(CONFIG_ISDN) += isdn.o
isdn-objs := isdn_net_lib.o isdn_v110.o isdn_common.o
3、目錄的迭代
obj-$(CONFIG_EXT2_FS) += ext2/
如果CONFIG_EXT2_FS 的值爲y或m,kbuild將會將ext2目錄列入向下迭代的目標中,但是其作用也僅限於此,具體ext2目錄下的文件是要作爲模塊編譯還是鏈入內核,還是有ext2目錄下的Makefile文件的內容來決定的
4、不同的模塊編譯方式
編譯模塊的時候,你可以將模塊放在代碼樹中,用Make modules的方式來編譯你的模塊,你也可以將模塊相關文件目錄放在代碼樹以外的位置,用如下命令來編譯模塊:
make -C path/to/kernel/src M=$PWD modules
-C指定內核源碼的根目錄,$PWD 或 `PWD` 是當前目錄的環境變量,告訴kbuild回到當前目錄來執行build操作。
5、模塊安裝
當你需要將模塊安裝到非默認位置的時候,你可以用INSTALL_MOD_PATH 指定一個前綴,如:
make INSTALL_MOD_PATH=/foo modules_install
模塊將被安裝到 /foo/lib/modules目錄下


注意2.6和2.4版本編譯結果的不同,內核模塊在2.6下是.ko後綴,取代了2.4下的.o後綴,使內核模塊和普通的目標文件區別開。(比較好呀)
發佈了12 篇原創文章 · 獲贊 77 · 訪問量 125萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章