在開發linux核心驅動時,免不了要接觸到makefile的編寫和修改,儘管網上的makefile模板一大堆,做一些簡單的修改就能用到自己的專案上,但是,對於這些基礎的東西,更應該做到知其然並知其所以然。
本篇文章中只討論linux核心模組編譯的makefile,linux核心makefile總覽可以參考另一篇部落格:linux核心makefile概覽
本篇部落格參考官方文件。
linux核心使用的是kbuild編譯系統,在編譯可載入模組時,其makefile的風格和常用的編譯c程式的makefile有所不同,儘管如此,makefile的作用總歸是給編譯器提供編譯資訊。
我們先來看看乙個最簡單的makefile是怎樣的:
obj-m+=hello.o
all:
make -c /lib/modules/$(shell uname -r)/build/ m=$(pwd) modules
clean:
make -c /lib/modules/$(shell uname -r)/build/ m=$(pwd) clean
這個makefile的作用就是編譯hello.c檔案,最終生成hello.ko檔案。
obj-m+=hello.o
obj-m表示編譯生成可載入模組。
相對應的,obj-y表示直接將模組編譯進核心。
可以看到,這裡並沒有輸入hello.c原始檔,熟悉makefile的人應該知道,這得益於makefile的自動推導功能,需要編譯生成filename.o檔案而沒有顯示地指定filename.c檔案位置時,make查詢filename.c是否存在,如果存在就正常編譯,如果不存在,則報錯。
obj-m+=hello.o,這條語句就是顯式地將hello.o編譯成hello.ko,而hello.o則由make的自動推導功能編譯hello.c檔案生成。
all,clean
all,clean這一類的是makefile中的偽目標,偽目標並不是乙個真正的編譯目標,它代表著一系列你想要執行的命令集合,通常乙個makefile會對應多個操作,例如編譯,清除編譯結果,安裝,就可以使用這些偽目標來進行標記。在執行時就可以鍵入:
make clean
make install
等指令來完成相應的指令操作,當make後不帶引數時,預設執行第乙個偽目標的操作。
make -c /lib/modules/$(shell uname -r)/build/ m=$(pwd) modules
標準的make指令是這樣的:make -c $kdir m=$pwd [target],下面分別介紹每個欄位的含義。
-c選項:此選項指定核心原始碼的位置,make在編譯時將會進入核心原始碼目錄,執行編譯,編譯完成時返回。
$kdir:/lib/modules/$(shell uname -r)/build/,指定核心原始碼的位置。
直接在目標板上編譯時,核心標頭檔案預設存放在/lib/modules/$(shell uname -r)/build/中,這個build/目錄是乙個軟連線,鏈結到原始碼標頭檔案的安裝位置。而核心真正的原始碼庫則直接引用正在執行的核心映象。
當跨平台編譯時,就需要指定相應的核心原始碼目錄,而不是系統中的原始碼目錄,但是交叉編譯時,需不需要指定架構平台和交叉編譯工具鏈呢?我們接著往下看;
[target]:modules,事實上,這是個可選選項。預設行為是將原始檔編譯並生成核心模組,即module(s),但是它還支援一下選項:
首先,當乙個.o目標檔案的生成依賴多個原始檔時,顯然make的自動推導規則就力不從心了(它只能根據同名推導,比如編譯filename.o,只會去查詢filename.c),我們可以這樣指定:
obj-m += hello.o
hello-y := a.o b.o hello_world.o
hello.o目標檔案依賴於a.o,b.o,hello_world.o,那麼這裡的a.o和b.o如果沒有指定原始檔,根據推導規則就是依賴原始檔a.c,b.c,hello_world.c.
除了hello-y,同時也可以用hello-objs,實現效果是一樣的。
kbuild支援同時編譯多個可載入模組,也就是生成多個.ko檔案,它的格式是這樣的:
obj-m := foo.o bar.o
foo-y := bar-y :=
就是這麼簡單。
通常,標準的makefile會寫成這樣:
ifneq ($(kernelrelease),)
obj-m := hello.o
else
kdir ?= /lib/modules/`uname -r`/build
all:
$(make) -c $(kdir) m=$(pwd) modules
clean:
$(make) -c $(kdir) m=$(pwd) clean
endif
為什麼要新增乙個ifneq,else,all條件判斷。
這得從linux核心模組make執行的過程說起:當鍵入make時,make在當前目錄下尋找makefile並執行,kernelrelease在頂層的makefile中被定義,所以在執行當前makefile時kernelrelease並沒有被定義,走else分支,直接執行
$(make) -c $(kdir) m=$(pwd) modules
而這條指令會進入到$(kdir)目錄,呼叫頂層的makefile,在頂層makefile中定義了kernelrelease變數.
在頂層makefile中會遞迴地再次呼叫到當前目錄下的makefile檔案,這時kernelrelease變數已經非空,所以執行if分支,在可載入模組編譯列表新增hello模組,由此將模組編譯成可載入模組放在當前目錄下。
歸根結底,各級子目錄中的makefile檔案的作用就是先切換到頂層makefile,然後通過obj-m在可載入模組編譯列表中新增當前模組,kbuild就會將其編譯成可載入模組。
如果是直接編譯整個核心原始碼,就省去了else分支中進入頂層makefile的步驟。
需要注意的乙個基本概念是:每一次編譯,頂層makefile都試圖遞迴地進入每個子目錄呼叫子目錄的makefile,只是當目標子目錄中沒有任何修改時,預設不再進行重複編譯以節省編譯時間。
當編譯的目標模組依賴多個標頭檔案時,kbuild對頭檔案的放置有這樣的規定:
linux核心makefile總覽可以參考另一篇部落格:linux核心makefile概覽
LINUX下可載入模組的幾個工具
當我們在make menuconfig把一些原始碼檔案以作為編譯控制項時,表示不編譯核心,而是以模組 ko 存在.此時,要生成.ko模組檔案,需要借助下面的命令 bash cd usr src linux x.y.z bash make modules上述命令幫助我們生成目標核心的.ko模組檔案.如...
linux 核心模組載入 HelloWorld
1。建立hello目錄 mkdir hello 2。進入hello目錄 cd hello 3。編寫hello.c include include module license gpl static int hello init void static void hello exit void mod...
Linux核心模組靜態載入的順序
linux驅動先註冊匯流排,匯流排上可以先掛device,也可以先掛driver,那麼究竟怎麼控制先後的順序呢?linux系統使用兩種方式去載入系統中的模組 動態和靜態。靜態載入 將所有模組的程式編譯到linux核心中,由do initcall函式載入 核心程序 init main.c kernel...