linux內核模塊編譯makefile

linux內核可加載模塊的makefile

在開發linux內核驅動時,免不了要接觸到makefile的編寫和修改,儘管網上的makefile模板一大堆,作一些簡單的修改就能用到本身的項目上,可是,對於這些基礎的東西,更應該作到知其然並知其因此然。
本篇文章中只討論linux內核模塊編譯的makefile,linux內核makefile總覽能夠參考另外一篇博客:linux內核makefile概覽html

本篇博客參考官方文檔linux

linux內核使用的是kbuild編譯系統,在編譯可加載模塊時,其makefile的風格和經常使用的編譯C程序的makefile有所不一樣,儘管如此,makefile的做用總歸是給編譯器提供編譯信息。git

最簡單的makefile

咱們先來看看一個最簡單的makefile是怎樣的:github

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文件。shell

obj-m+=hello.o

obj-m表示編譯生成可加載模塊。架構

相對應的,obj-y表示直接將模塊編譯進內核。工具

能夠看到,這裏並無輸入hello.c源文件,熟悉makefile的人應該知道,這得益於makefile的自動推導功能,須要編譯生成filename.o文件而沒有顯示地指定filename.c文件位置時,make查找filename.c是否存在,若是存在就正常編譯,若是不存在,則報錯。ui

obj-m+=hello.o,這條語句就是顯式地將hello.o編譯成hello.ko,而hello.o則由make的自動推導功能編譯hello.c文件生成。spa

all,clean

all,clean這一類的是makefile中的僞目標,僞目標並非一個真正的編譯目標,它表明着一系列你想要執行的命令集合,一般一個makefile會對應多個操做,例如編譯,清除編譯結果,安裝,就可使用這些僞目標來進行標記。在執行時就能夠鍵入:code

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/目錄是一個軟鏈接,連接到源碼頭文件的安裝位置。而內核真正的源碼庫則直接引用正在運行的內核鏡像。

當跨平臺編譯時,就須要指定相應的內核源碼目錄,而不是系統中的源碼目錄,可是交叉編譯時,需不須要指定架構平臺和交叉編譯工具鏈呢?咱們接着往下看;

M=$(PWD):須要編譯的模塊源文件地址


[target]:modules,事實上,這是個可選選項。默認行爲是將源文件編譯並生成內核模塊,即module(s),可是它還支持一下選項:

  • modules_install:安裝這個外部模塊,默認安裝地址是/lib/modules/$(uname -r)/extra/,同時能夠由內建變量INSTALL_MOD_PATH指定安裝目錄
  • clean:卸載源文件目錄下編譯過程生成的文件,在上文的makefile最後一行能夠看到。
  • help:幫助信息

更多選項

編譯多個源文件

hello_world老是簡單的,可是在實際開發中,就會出現更復雜的狀況,這時候就須要瞭解更多的makefile選項:

首先,當一個.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 := <foo_srcs>
    bar-y := <bar_srcs>

就是這麼簡單。

ifneq ($(KERNELRELEASE),)

一般,標準的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,只是當目標子目錄中沒有任何修改時,默認再也不進行重複編譯以節省編譯時間。

這裏同時解決了上面的一個疑問:既然是從頂層目錄開始編譯,那麼只要頂層目錄中指定了架構(ARCH)和交叉編譯工具鏈地址(CROSS_COMPILE),各子目錄中就再也不須要指定這兩個參數。

頭文件的放置

當編譯的目標模塊依賴多個頭文件時,kbuild對頭文件的放置有這樣的規定:

  • 直接放置在makefile同在的目錄下,在編譯時當前目錄會被添加到頭文件搜索目錄。

  • 放置在系統目錄,這個系統目錄是源代碼目錄中的include/linux/。

  • 與通用的makefile同樣,使用-I$(DIR)來指定,不一樣的是,表明編譯選項的變量是固定的,爲ccflag.

    通常的用法是這樣的:
    
              ccflags-y := -I$(DIR)/include   
      kbuild就會將$(DIR)/includ目錄添加到編譯時的頭文件搜索目錄中。

linux內核makefile總覽能夠參考另外一篇博客:linux內核makefile概覽

好了,關於linux編譯內核模塊的makefile介紹就到此爲止啦,若是朋友們對於這個有什麼疑問或者發現有文章中有什麼錯誤,歡迎留言

原創博客,轉載請註明出處!

祝各位早日實現項目叢中過,bug不沾身.

相關文章
相關標籤/搜索