Linux 內核 Makefile 體系簡單分析

摘自:http://blog.chinaunix.net/uid-26806098-id-3141136.html

衆所周知,Linux內核是使用make命令來配置並編譯的,那必然少不了Makefile。在內核目錄樹中我們可以看到內核編譯系統的頂層Makefile文件。但是如此複雜、龐大的內核源碼絕不可能使用一個或幾個Makefile文件來完成配置編譯,而是需要一套同樣複雜、龐大,且爲Linux內核定製的Makefile系統。她可以說是內核的一個子系統,是內核中比較特殊的一部分,幾乎都是應用層的程序和腳本,但又和生成的內核二進制文件息息相關。編譯不僅涉及本地編譯,還涉及各個平臺之間的交叉編譯以及二進制文件格式處理等等。她是對Makefile 在功能上的擴充,使其在配置編譯Linux內核的時候更加靈活、高效和簡潔。

儘管她是一個複雜的系統,但對絕大部分內核開發者來說只需要知道如何使用,而無需瞭解其中的細節。她對絕大部分內核開發者基本上是透明的,隱藏了大部分實現細節,有效地降低了開發者的負擔,能使其能專注於內核開發,而不至於花費時間和精力在編譯過程上。

以下我們就來簡要的瞭解一下內核Makefile體系。

一、  內核Makefile體系概述

其實內核Makefile體系的包含了KconfigKbuild兩個系統。她曾經的維護人是Sam Ravnborg <[email protected]>,現在的暫時沒有查到。參考資料:

kbuild 更換維護者   作者:王聰 (西郵神人,崇拜下)

Kconfig 對應的是內核配置階段,如你使用命令:make menuconfig,就是在使用Kconfig系統。Kconfig由以下三部分組成:

scripts/kconfig/*

Kconfig文件解析程序

kconfig  

各個內核源代碼目錄中的kconfig文件

arch/$(ARCH)/configs/*_defconfig

各個平臺的缺省配置文件

Kconfig系統生成.config後,Kbuild 會依據.config編譯指定的目標。後面我會簡單地對make %config的流程進行情景分析,這裏不必贅述。

Kbuild 是內核Makefile體系重點,對應內核編譯階段,由5個部分組成:

頂層Makefile

根據不同的平臺,對各類target分類並調用相應的規則Makefile生成目標

.config

內核配置文件

arch/$(ARCH)/Makefile

具體平臺相關的Makefile

scripts/Makefile.*

通用規則文件,面向所有的Kbuild Makefiles,所起的作用可以從後綴名中得知。

各子目錄下的Makefile 文件

由其上層目錄的Makefile調用,執行其上層傳遞下來的命令

而其中scripts目錄下的編譯規則文件和其目錄下的C程序在整個編譯過程起着重要的作用。列舉如下:

文件名

作用

Kbuild.include

共用的定義文件,被許多獨立的Makefile.*規則文件和頂層Makefile包含

Makefile.build

提供編譯built-in.o, lib.a規則

Makefile.lib

負責歸類分析obj-yobj-m和其中的目錄subdir-ym所使用的規則

Makefile.host

本機編譯工具(hostprog-y)的編譯規則

Makefile.clean

內核源碼目錄清理規則

Makefile.headerinst

內核頭文件安裝時使用的規則

Makefile.modinst

內核模塊安裝規則

Makefile.modpost

模塊編譯的第二階段,<module>.o<module>.mod生成<module>.ko時使用的規則

頂層Makefile 主要是負責完成vmlinux(內核文件)*.ko(內核模塊文件的編譯。頂層 Makefile 讀取.config 文件,並根據.config 文件確定訪問哪些子目錄,並通過遞歸向下訪問子目錄的形式完成。頂層Makefile同時根據.config 文件原封不動的包含一個具體架構的Makefile,其名字類似於arch/$(ARCH)/Makefile。該架構Makefile 向頂層Makefile 提供其架構的特別信息。

每一個子目錄都有一個Makefile 文件,用來執行從其上層目錄傳遞下來的命令。子目錄的 Makefile 也從.config 文件中提取信息,生成內核編譯所需的文件列表。


二、 內核Makefile導讀與情景分析

1、概述

上面簡要介紹了內核Makefile的總體結構,但當我們打開頂層Makefile文件時還是因爲她的複雜而覺得無從下手。但是內核Makefile就是Makefile,和最簡單的Makefile遵循着同樣的規則。所以只要我們靜下心來分析,還是可以理解的。當然,在閱讀內核的Makefile前,你必須對Makefileshell腳本有一定的基礎。

  1. 推薦參考資料:
  2. GNU make中文手冊》 翻譯整理:徐海兵  PDF文檔
  3. 高級Bash腳本編程指南》翻譯:楊春敏 黃毅

根據Makefile的執行規則,在分析Makefile時,首先必須確定一個目標 ,然後才能確定所有的依賴關係 ,最後根據更新情況決定是否執行相應的命令。所以要看懂內核Makefile的大致框架,我們首先要了解她裏面所定義的目標。而內核Makefile所定義的目標基本上可以通過 make help打印出來(因爲help本身就是頂層Makefile的一個目標,裏面是打印幫助信息的“echo”命令)。

這些目標可以分爲以下幾個大類:

目標

常用目標舉例

作用

配置

%config

config

啓動Kconfig,以不同界面來配置內核。

menuconfig

xconfig

編譯

all

編譯vmlinux內核映像和內核模塊

vmlinux

編譯vmlinux內核映像

modules

編譯內核模塊

安裝

headers_install

安裝內核頭文件/模塊

modules_install

源碼瀏覽

tags

生成代碼瀏覽工具所需要的文件

TAGS

cscope

靜態分析

checkstack

檢查並分析內核代碼

namespacecheck

headers_check

內核打包

%pkg

以不同的安裝格式編譯內核

文檔轉換

%doc

kernel文檔轉成不同格式

構架相關

(以arm爲例)

zImage

生成壓縮的內核映像

uImage

生成壓縮的u-boot可引導的內核映像

install

安裝內核映像

其中的構架相關目標在頂層Makefile上並出現,而是被包含在平臺相關的Makefilearch/$(ARCH)/Makefile)中。

2、情景分析

以下我們就來分析一個簡單的目標(menuconfig),作爲情景分析範例來演示一下內核Makefile的分析方法。

首先當我們在內核源碼的根目錄下執行make menuconfig命令時,根據規則,make程序讀取頂層Makefile 文件及其包含的Makefile文件,內建所有的變量、明確規則和隱含規則,並建立所有目標和依賴之間的依賴關係結構鏈表。make程序最終會調用規則:

config %config: scripts_basic outputmakefile FORCE

       $(Q)mkdir -p include/linux include/config

       $(Q)$(MAKE) $(build)=scripts/kconfig $@

調用的原因是我們指定的目標“menuconfig”匹配了“%config”。

她的依賴目標是scripts_basic outputmakefile,以及FORCE。也就是說在完成了這3個依賴目標後,下面的兩個命令纔會執行以完成我們指定的目標“menuconfig”。

所以我們來看看這三個依賴目標實現的簡要過程:

1scripts_basic

make程序會調用規則:

scripts_basic:

       $(Q)$(MAKE) $(build)=scripts/basic




他沒有依賴目標,所以直接執行了以下的指令,只要將指令展開,我們就知道make做了什麼操作。其中比較不好展開的是$(build),她的定義在scripts/Kbuild.include中:

build := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build obj



所以展開後是:

make -f scripts/Makefile.build obj= scripts/basic


也就是make 解析執行scripts/Makefile.build文件,且參數obj= scripts/basic。而在解析執行scripts/Makefile.build文件的時候,scripts/Makefile.build又會通過解析傳入參數來包含對應文件夾下的Makefile文件(scripts/basic/Makefile),從中獲得需要編譯的目標。

在確定這個目標以後,通過目標的類別來繼續包含一些scripts/Makefile.*文件。例如scripts/basic/Makefile中內容如下:

hostprogs-y := fixdep docproc hash

always      := $(hostprogs-y)

 

# fixdep is needed to compile other host programs

$(addprefix $(obj)/,$(filter-out fixdep,$(always))): $(obj)/fixdep






所以scripts/Makefile.build會包含scripts/Makefile.host。相應的語句如下:

# Do not include host rules unless needed

ifneq ($(hostprogs-y)$(hostprogs-m),)

include scripts/Makefile.host

endif





此外scripts/Makefile.build會包含include scripts/Makefile.lib等必須的規則定義文件在這些文件的共同作用下完成對scripts/basic/Makefile中指定的程序編譯。

由於Makefile.build的解析執行牽涉了多個Makefile.*文件,過程較爲複雜,礙於篇幅無法一條一條指令的分析,興趣的讀者可以自行分析。

  1. 推薦兩篇經典的分析文檔:
  2. kbuild實現分析》 作者:[email protected]
  3. 《Kbuild系統原理分析》  作者未知,網上有PDF文檔

2outputmakefile

make程序會調用規則:

PHONY += outputmakefile

# outputmakefile generates a Makefile in the output directory, if using a

# separate output directory. This allows convenient use of make in the

# output directory.

outputmakefile:

ifneq ($(KBUILD_SRC),)

    $(Q)ln -fsn $(srctree) source

    $(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile

        $(srctree) $(objtree) $(VERSION) $(PATCHLEVEL)

endif











從這裏我們可以看出:outputmakefile是當KBUILD_SRC 不爲空(指定O=dir,編譯輸出目錄和源代碼目錄分開)時,在輸出目錄建立Makefile時才執行命令的,

如果我們在源碼根目錄下執行make menuconfig命令時,這個目標是空的,什麼都不做。

如果我們指定了O=dir時,就會執行源碼目錄下的scripts/mkmakefile,用於在指定的目錄下產生一個Makefile,並可以在指定的目錄下開始編譯。

3FORCE

這是一個在內核Makefile中隨處可見的僞目標,她的定義在頂層Makefile的最後:

PHONY += FORCE

FORCE:

 




是個完全的空目標,但是爲什麼要定義一個這樣的空目標,並讓許多目標將其作爲依賴目標呢?原因如下:

正因爲FORCE 是一個沒有命令或者依賴目標,不可能生成相應文件的僞目標。當make執行此規則時,總會認爲FORCE不存在,必須完成這個目標,所以她是一個強制目標。也就是說:規則一旦被執行,make 就認爲它的目標已經被執行並更新過了。當她作爲一個規則的依賴時,由於依賴總被認爲被更新過的,因此作爲依賴所在的規則中定義的命令總會被執行。所以可以這麼說:只要執行依賴包含FORCE的目標,其目標下的命令必被執行。

make完成了以上3個目標之後,就開始執行下面的命令的,首先是

$(Q)mkdir -p include/linux include/config


這個很好理解,就是建立兩個必須的文件夾。然後

$(Q)$(MAKE) $(build)=scripts/kconfig $@

      

這和我們上面分析的$(Q)$(MAKE) $(build)=結構相同,將其展開得到:

make -f scripts/Makefile.build obj=scripts/kconfigmenuconfig

      

所以這個指令的效果是使make 解析執行scripts/Makefile.build文件,且參數obj=scripts/kconfig menuconfig。這樣Makefile.build會包含對應文件夾下的Makefile文件(scripts/kconfig /Makefile),並完成scripts/kconfig /Makefile下的目標:

menuconfig: $(obj)/mconf

            $< $(Kconfig)

      


這個目標的依賴條件是$(obj)/mconf,通過分析可知她其實是對應以下規則:

mconf-objs  := mconf.o zconf.tab.o $(lxdialog)

……

ifeq ($(MAKECMDGOALS),menuconfig)

    hostprogs-y += mconf

endif

      





也就是編譯生成本機使用的mconf程序。完成依賴目標後,通過scripts/kconfig /Makefile中對Kconfig的定義可知,最後執行:

mconf arch/$(SRCARCH)/Kconfig


而對於confxconf等都有類似的過程,所以總結起來:當make %config 時,內核根目錄的頂層Makefile會臨時編譯出scripts/kconfig 中的工具程序conf/mconf/qconf 等負責對arch/$(SRCARCH)/Kconfig 文件進行解析。這個Kconfig 又通過source標記調用各個目錄下的Kconfig文件構建出一個Kconfig,使得工具程序構建出整個內核的配置界面。在配置結束後,工具程序就會生成我們常見的.config文件。

三、在內核中添加自己的模塊

雖然內核Makefile體系很是複雜,但是這種複雜帶來的確是開發時的便利。其實內核Makefile體系之所以複雜,其中的一個原因就是爲了方便擴展。對於一個開發者來要在內核中添加自己的一個驅動代碼是非常簡單的事情。

一般來說,對於一個新驅動代碼的添加,驅動工程師只需要在內核源碼的drivers目錄的相應子目錄下添加新設備驅動源碼,並增加或修改該目錄下的KconfigMakefile文件即可。

比如你已經寫好了一個針對TI 的AM33XX芯片的 LED的驅動程序,名爲am33xx_led.c

(1)       將驅動源碼am33xx_led.c等文件複製到linux-X.Y.Z/drivers/char目錄。

(2)       在該目錄下的Kconfig文件中添加LED驅動的配置選項:

config AM33XX_LED

       bool "Support for am33xx led drivers"

       depends on  SOC_OMAPAM33XX

       default n

       ---help---

          Say Y here if you want to support for AM33XX LED drivers.








(3)     在該目錄下的Makefile文件中添加對LED驅動的編譯:

obj-$(CONFIG_AM33XX_LED)   +=  am33xx_led.o


這樣你就可以在make menuconfig的時候看到這個配置選項,並進行配置了。

當然,上面的例子只是一個意思,對於Kconfig文件和Makefile的詳細語法,請參考內核文檔:Documentation/kbuild/makefile.txt

四 、在內核Makefile上對讀者的建議

這個複雜的Makefile體系體現了很多優秀程序共有的設計思想,對於我們今後的程序設計上有很多值得借鑑的地方。比如:模塊化設計、簡化編程接口,使得自行添加模塊更加的簡潔。閱讀分析這樣複雜的Makefile對於學習和編寫Makefileshell腳本有很好的參考價值。如果你正在學習Makefile的編寫和閱讀,那你可以耐心的分析一下內核的Makefile體系,只要你認真分析了一兩個目標的實現,你會發現當你在閱讀一些小軟件的Makefile時已經是輕車熟路了。特別是現在很多芯片的開發包都是以SDK包的形式發佈的,而這些軟件包都是通過Makefile體系來實現自動編譯和配置的,所以熟悉Makefile是每個Linux開發者都需要做到的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章