天天看點

Linux核心建構系統之二

Linux核心建構系統所支援的目标 

由前面的概述可以知道,不管是kconfig步驟、還是kbuild步驟、還是安裝都可藉由"make targets"形式的指令來完成。是以,分析一下所有可能的targets是必要的。核心建構系統所支援的目标完整清單可由指令 "make help" 列印出來。這裡僅簡單的列出最重要的部分:

​​​​

這裡咱們先澄清一個觀念。很多人認為核心建構系統僅僅是kbuild,其實不然。你由上面這些目标可以很清楚的看出來,整個核心建構系統實作了不僅build,而且還包括config和install。那些認為核心建構系統僅僅是 kbuild 的人,可能是受到這個詞的中英文翻譯的迷惑。

為了幫助大家對後面内容的了解,我們将核心建構系統所支援的所有目标分一下類。出現在 make 指令後面的内容總共有這麼幾個類别:

和 .config 完全無關的目标

這些目标既不會産生 .config 檔案,也不需要使用 .config 檔案。包括上面列出來的clean,mrproper,distclean,headers_install,kernelrelease,kernelversion等等。

和 .config 相關的目标

除了和.config完全無關的那些目标外,其它目标皆是要麼會産生.config檔案,要麼需要使用.config檔案。前者又稱之為配置目标(config targets),後者為建構目标(build targets)。

前者主要是:config 和 %config。百分号是通配符,%config指代了一系列的以config為結尾的目标,包括menuconfig,oldconfig,silentoldconfig,s3c2410_defconfig等等。

後者主要是:all,vmlinux,modules,zImage,uImage等等。它們的産生需要以.config檔案中的配置結果作為依據。

single targets

除了上面這幾個外,上面清單中還有一類目标沒有歸納進來。那就是dir/,dir/file.[ois],dir/file.ko等目标。這些目标不同于那些複合內建性的目标,它們以單個檔案或目錄的形式存在,比方源碼樹中的某一個小目錄,某一個單獨的對象檔案,某一個外部子產品檔案等等。

值得一說的是,出現在make 指令後面的内容,可以是上面說到的這些目标的單獨形式,也可以是這些目标的混合形式。比方我使用一個混合形式的指令 ”make oldconfig all“來既做config,又做build。

另外,make 指令後面除跟目标外,還可以跟一些變量定義作為選項,主要有這麼幾個:

V變量

make v=0 [targets] 簡化編譯所輸出的内容

make V=1 [targets] 詳細輸出

make V=2 [targets] 編譯過程中會編譯一系列子目标,用這個選項會列出之是以編譯它們的原因

O變量

make O=dir [target] 編譯過程中,将所有中間檔案和目标檔案(包括.config檔案)存到dir目錄中, 而不是像預設的那樣放在核心源代碼樹中。

編譯外部子產品時用的M變量和SUBDIRS變量

make -C KERNELDIR M=dir modules

make -C KERNELDIR SUBDIRS=dir modules

其中 KERNELDIR 為核心代碼樹目錄,dir為外部子產品源代碼所在目錄。如果是給你自己機器上正在運作着的核心編譯子產品,那麼KERNELDIR一般取值 /lib/modules/`uname -r`/build 。

構成核心建構系統的檔案 

知道核心建構系統所支援的目标之後,我們再來看看核心建構系統的組成。Linux kernel無疑是複雜的,那建構核心的工具自然也一定是相對複雜的,事實上也是如此,它由下面這一系列的檔案所組成:

頂層 Makefile;

提供針對各種目标的接口,一般和實作無關。當我們要針對某個目标進行分析時,作為起點,總是嘗試在此檔案中找到對應的目标定義,然後沿着該定義深入挖掘。

平台相關的 Makefile;

提供針對不同種架構的目标,變量和規則定義。其檔案位置比較固定,通常位于 arch/*/ 目錄下面,即 arch/*/Makefile 檔案。

各類規則定義檔案;

在 script 目錄下面,有很多定義了不同方面規則的檔案。這些檔案可視作對頂層Makefile内所設計接口的實作,它們包括:

Kbuild.include

通用的定義。這個檔案被其他Makefile所包含,比方被頂層Makeile,被Makefile.build等包含。

Makefile.build

這個檔案定義了許多編譯規則,用于編譯 built-in.o, lib.a 等元件。後面會知道,正是這些元件構成了Linux核心映像。

Makefile.lib

這個檔案給很多變量賦了值,比方obj-y,obj-m,subdir-y,subdir-m等等,這些變量所指代的一系列檔案(目錄)清單都是需要編譯處理的。另外該檔案還包括了很多cmd指令的定義,在編譯核心的過程中搭配 if_changed系列使用。

Makefile.host

在Linux核心的編譯過程但中,不僅需要使用各種諸如as,gcc,ld,tar之類的工具,而且還需要用到很多其它程式來處理源代碼。比方需要用fixdep來處理目标的依賴關系,需要使用conf/gconf/mconf等工具來解析衆多Kconfig配置檔案中的配置選項以生成.config檔案,需要使用genksyms來計算核心導出符号的校驗和(checksum)等等。這些工具也以源代碼的形式放在script的不同子目錄下面,是以在核心編譯過程中,核心建構系統需要先行将它們編譯出來。那麼檔案Makefile.host内就定義了如何将它們編譯成可執行程式的相關規則。

Makefile.clean

和一般應用程式的編譯開發一樣,核心編譯後産生的結果需要經常被清除。這個檔案内詳細定義了做clean所需要的相關規則。

Makefile.headerisnt

定義了如何安裝頭檔案的規則。

Makefile.modinst

前面已經說過什麼是子產品。其實,在我們的實際開發中,通常包含兩個部分的子產品。一部分是核心源代碼中實際上已經實作的子產品,這部分在核心建構過程中會編譯出來,我們且稱之為内部子產品;另外一部分是作為驅動程式開發者的我們自己寫出來的,通常被放在核心源碼樹之外目錄中的子產品,我們稱之為外部子產品,需要我們自己單獨編譯。檔案Makefile.modinst定義了如何安裝内部子產品到INSTALL_MOD_PATH所指定目錄中去的相關規則。外部子產品一般需要靠我們自己手動拷貝到那個目錄下面去。

Makefile.modpost

在2.6核心中,子產品的編譯需要兩個階段。第一階段需要将對應的子產品源代碼編譯成.o檔案,第二階段再将對應的.o和另外産生的.mod.o連結起來,形成.ko核心子產品檔案。此檔案内定義的規則負責完成這第二階段的工作。

Makefile.fwinst

現在要使某些裝置能夠正常工作的話,單單有核心裡面的驅動程式是不夠的,它們還需要另外一種稱之為firmware的東西,這其實是一段二進制資料。在使裝置能正常工作之前,裝置的驅動程式需要先負責将這段資料寫入裝置才行。大部分firmware代碼都是私有的,其所使用的License和GPL相沖突,是以核心社群裡一直有激烈的争論是否要将fireware從GPLed的核心代碼中去除(http://lwn.net/Articles/284932/)。在2.6.27版本之前,firmware 和驅動程式代碼一起編譯,但是在2.6.27版本中,驅動程式目錄drivers/下的firmware被取出,單獨放到firmware/目錄中。此Makefile的功能是将firmware映像安裝到檔案系統的特定目錄下,以友善驅動程式在适單的時候找到并使用它們。

代碼樹下各目錄中的Makefile

核心的編譯建構是一個不斷遞歸進入不同子目錄繼續編譯的過程。每個子目錄下面均需要有一個Makefile負責設定該目錄下需要編譯哪些源檔案,這種設定通常取決于kconfig配置的結果。這個Makefile的名字一般就是"Makefile",但是名字“Kbuild”也可以使用,是以在核心文檔Documentation/kbuild/makefiles.txt中将它們總稱為"kbuild makefiles"。有時候,"Makefile"這個名字已經作為别的用途被使用掉了,那麼就隻好用"Kbuild"來命名。比方在arch/x86/目錄下面,已經有一個”Makefile“充當架構平台相關的Makefile來使用了,那麼就需要換個名字"Kbuild"。

代碼樹各目錄中的一系列配置檔案 KCconfig

這些配置檔案其實并不直接參與核心的建構。他們都是為了完成kconfig配置過程準備的。核心開發者事先将可以選擇的選項及相關說明以一定的格式包括在這一系列KConfig中,然後在使用者使用"make menuconfig"類似指令進行配置過程中,核心建構系統将這些選項從這系列配置檔案中讀出來呈現在螢幕上,之後使用者進行操作以進行不同選項的設定。設定完最後的結果被儲存在.config檔案中,以供後面的kbuild過程使用。

在這衆多檔案中,最主要的角色自然是頂層目錄下的Makefile,其他的檔案都或直接、或間接的和它相關聯。這些檔案之間的關聯可以列出如下:

​​​​

如上示,我們可以将這些關聯分成兩類:

直接包含

在一個檔案中,利用include來包含另外的檔案。這是比較簡單的,比如字母 I,L,H 就表示了對應的檔案,這裡比方scripts/Makefile.build直接包含了scripts/Kbuild.include, scripts/Makefile.lib 和 scripts/Makefile.host。字母K 表示了它還包含核心樹中其他子目錄下的 Kbuild/Makefile 檔案。

使用 make -f 來調用

-f 是使用不同makefile檔案來進行make的選項,請參見我們的JulWiki。這種方式在核心中用的挺多,比方上面的 Cmp, Cfi表示頂層 Makefile 調用了 scripts/Makefile.modpost 和 scripts/Makefile.fwinst。其調用的代碼比方為:

​​​​

有時候 "-f ... obj" 的部分被一個變量定義所取代。比方上面的 Cb,Cc,Chi 表示頂層Makefile調用了 scripts/Makefile.build, scripts/Makefile.clean 和 scripts/Makefile.headersinst,調用的代碼分别舉例為:

​​​​

​​​​

​​​​

上面代碼揭示了所使用的變量分别為 $(build), $(clean)和$(hdr-inst)。 而它們被定義在不同的檔案裡,比如 $(build) 被定義在 scripts/Kbuild.include中,具體代碼為:

​​​​

這種調用的方式在整個核心建構系統中非常普遍,是以需要先有個了解。實際上,變量的等号後面常跟有一個目錄,而這個目錄中一般都有一個Kbuild/Makefile,也就是上面所說的所謂“”各子目錄下的Kbuild/Makefile”。如果你繼續追蹤到 scripts/Makefile.build 檔案裡,你會發現是她直接包含了這些”各子目錄下的Kbuild/Makefile“。下面這些就是蹤迹所在:

繼續閱讀