序言
從linux核心2.6開始,linux核心的編譯採用kbuild系統,這同過去的編譯系統有很大的不同,尤其對於linux核心模組的編譯。在新的系統下,linux編譯系統會兩次掃瞄linux的makefile:首先編譯系統會讀取linux核心頂層的makefile,然後根據讀到的內容第二次讀取kbuild的makefile來編譯linux核心。
linux核心makefile分類
kernel makefile位於linux核心源**的頂層目錄,也叫 top makefile。它主要用於指定編譯linux kernel目標檔案(vmlinux)和模組(module)。這編譯核心或模組是,這個檔案會被首先讀取,並根據讀到的內容配置編譯環境變數。對於核心或驅動開發人員來說, 這個檔案幾乎不用任何修改。
kbuild系統使用kbuild makefile來編譯核心或模組。當kernel makefile被解析完成後,kbuild會讀取相關的kbuild makefile進行核心或模組的編譯。kbuild makefile有特定的語法指定哪些編譯進核心中、哪些編譯為模組、及對應的原始檔是什麼等。核心及驅動開發人員需要編寫這個kbuild makefile檔案。
arch makefile位於arch/$(arch)/makefile,是系統對應平台的makefile。kernel top makefile會包含這個檔案來指定平台相關資訊。只有平台開發人員會關心這個檔案。
kbuild makefile
kbuild makefile的檔名不一定是
makefile
,儘管推薦使用makefile這個名字。大多的kbuild檔案的名字都是makefile。為了與其他makefile檔案相區別,你也可以指定kbuild makefile的名字為
kbuild
。而且如果「makefile」和「kbuild」檔案同時存在,則kbuild系統會使用「kbuild」檔案。
kbuild makefile的乙個最主要功能就是指定編譯什麼,這個功能是通過下面兩個物件指定的obj-?和***-objs:
obj-?指定編譯什麼,怎麼編譯?其中的「?」可能是「y」或「m」,「y」指定把物件編譯進核心中,「m」指定把物件編譯為模組。語法如下;
obj-? = $(target).o
target為編譯物件的名字。如果沒有指定***-objs,這編譯這個物件需要的原始檔就是$(target).c或$(target).s。如果指定了$(target)-objs,則編譯這個物件需要的原始檔由$(target)-objs指定,並且不能有$(target).c或$(target).s檔案。
***-objs指定了編譯物件需要的檔案,一般只有在原始檔是多個時才需要它。
只要包含了這兩行,kbuild makefile就應該可以工作了。
有時乙個物件可能嵌入到另乙個物件的目錄下,那個如何編譯子目錄下的物件呢?其實很簡單,只要指定obj_?的物件為子目錄的名字就可以了:
obj-? = $(sub_target)/
其中「?」可以是「y」或「m」,$(sub_target)是子目錄名字。
儘管在大多數情況下不需要指定編譯器選項,有時我們還是需要指定一些編譯選項的。
這些編譯選項用於指定cc、as和ld的編譯選項
編譯外部模組
有時候我們需要在核心源**數的外面編譯核心模組,編譯的基本命令是:
make -c $(kernel_dir) m=`pwd` modules
我們可以把這個命令整合到makefile裡,這樣我們就可以只輸入「make」命令就可以了。回想上一章的那個makefile,它把normal makefile 和kbuild
makefile整合到乙個檔案中了。為了區別kbuild makefile 和normal makefile,這樣我們改寫makefile為如下形式,並且新增kbuild makefile - 「kbuild」。
##makefile
ifneq ($(kernelrelease),)
include "kbuild"
else
kernel_dir = /lib/modules/`uname -r`/build
moduledir := $(shell pwd)
.phony: modules
default: modules
modules:
make -c $(kernel_dir) m=$(moduledir) modules
clean distclean:
rm -f *.o *.mod.c .*.*.cmd *.ko
rm -rf .tmp_versions
endif
## kbuild
module_name = helloworld
$(module_name)-objs := hello.o
obj-m
:= $(module_name).o
一般不需要在makefile裡包含如下**,這樣寫完全是為了相容老版本的kbuild系統。kernelrelease變數在kernel makefile裡定義的,因此只有在第二次由kbuild讀取這個makefile檔案時才會解析到kbuild的內容。
ifneq ($(kernelrelease),)
include "kbuild"
else
...endif
外部標頭檔案
有時需要連線核心源**外部的系統標頭檔案,但kbuild系統預設的系統標頭檔案都在核心源**內部,如何使用外部的標頭檔案呢?這個可以借助於kbuild系統的特殊規則:
extra_cflags可以給kbuild系統新增外部系統標頭檔案,
extra_cflags += $(ext_include_path)
一般外部標頭檔案可能位於外部模組原始檔的目錄內,如何指定呢?這可以借助$(src)或$(obj)
$(src)是乙個相對路徑,它就是makefile/kbuild檔案所在的路徑。同樣$(obj)就是編譯目標儲存的路徑,預設就是源**所在路徑。
因此,我們修改kbuild檔案新增 extra_cflags 來包含外部標頭檔案儘管在這個驅動裡沒有引用外部系統標頭檔案:
## kbuild
module_name = helloworld
$(module_name)-objs := hello.o
extra_cflags := -i$(src)/include
obj-m
:= $(module_name).o
後記
這裡我們詳細的介紹了linux核心的kbuild系統,相信你已經可以自如的寫自己的kbuild makefile了。現在開始,我們就可以寫帶有一些功能的驅動程式了。linux裡的驅動程式可以分為三類,字元裝置驅動程式,塊裝置驅動程式和網路裝置驅動程式。在後面的章節裡你會詳細的了解到這些驅動程式,同時可以了解到linux核心裡用到的一些技術,如程序管理、記憶體管理、核心同步技術、核心時鐘等。
Linux驅動程式開發 002 Kbuild系統
序言 從linux核心2.6開始,linux核心的編譯採用kbuild系統,這同過去的編譯系統有很大的不同,尤其對於linux核心模組的編譯。在新的系統下,linux編譯系統會兩 次掃瞄linux的makefile 首 先編譯系統會讀取linux核心頂層的makefile,然後根據讀到的內容第二次讀...
linux 驅動程式 高階字元驅動程式
ioctl方法 驅動程式的原型實現 int ioctl struct inode inode,struct file filp,unsigned int cmd,unsigned long arg ioctl 命令選擇 位段結構 number direction ioc read ioc write...
讀書筆記 Linux裝置驅動程式(一) 0 02
驅動程式的引數值可由in od或者modprobe在裝載模組時設定,後者還可以從配置檔案 etc modules.conf 中獲得引數賦值。這些命令能夠在命令列中接受整型和字串型賦值。如 模組需要獲得乙個叫做skull ival的整型引數和乙個叫做skull sval的字串型引數 in od sku...