Linux內核構建系統原理

轉自: http://blog.csdn.net/woshixingaaa/article/details/5994494

Linux內核構建系統原理

分類: linux內核編程 205人閱讀 評論(0) 收藏 舉報

部分譯自:《Embedded Linux System Design and Development 》 BY P. Raghavan / Amol Lad / Sriram Neelakandan
劉建文略譯

內核 與應用程序分開構建
內核 與應用程序被設計成分開構建的,由C頭文件和C庫實現(KEMIN:我們一般都是基於庫編譯構建應用程序,這是默認的。但是如果沒有與實時系統 的統一構建方式對比,認識面很單一,難以較全面認識構建軟件的本質)。分開構建的優點是易於獨立開發新應用,滿足動態需求;缺點或代價是對內核 與應用的接口的維護。

舉一個具體例子詳述[分開構建 ]所帶來的不便。假設某OEM廠商要生產兩種功能類似的網絡產品:以太網橋(Ethernet bridge)和路由器(router),這兩項產品是基於同一個硬件設計的。由於兩種產品的硬件是一樣的,基本的系統 支撐軟件部分也是一樣的(比如boot loader和BSP),它們的不同只在於基於支撐軟件上的更高級一些軟件功能部分。因此,爲了節省開發成本,OEM廠商只爲它們維護一套單一的基本支撐軟件部分代碼,然後根據系統 選項分別爲它們構建特定功能軟件部分。系統 構建選項可通make實現,比如make bridge構建以太網橋,make router構建路由。要實現這兩個選項則要完成很多“不便”的工作(KEMIN:由此可見分開構建的方便所帶來的不便,代價代價!):

第一,爲內核 配置相應的協議軟件,比如以太網橋需要spanning bridge,路由需要IP協議;
第二,構建相應的應用軟件,比如路由器的路由服務進程(routed daemon);
第三,配置相應的啓動文件,比如網絡接口初始化文件;
第四,選擇相應的配置文件(比如HTML文件和CGI腳本)打包進根文件系統
用戶可能會問,爲什麼不把兩種產品所需的東西全打包進根文件系統 ,然後在運行時由產品自己判斷和執行所需要的東西呢?桌面系統 和服務器產品一般這樣做的,但嵌入式系統 與桌面系統 和服務器的需求不同,爲了節省資源,更多的組件配置發生在構建時,而不是運行時。

要實現[構建時的組件配置 ],需要什麼樣的機制呢?

內核 構建過程(用戶角度)
內核 構建系統 (kernel build system),行話kbuild,是和內核 源碼綁定在一起的基於GNU make的腳本系統 。通過構建系統 (以下簡稱kbuild),你可以輕易的對內核 進行組態(KEMIN:組態就是所謂的配置,個人覺得臺灣的這一譯法較爲形象和準確,爲了術語統一,以下還是使用配置一語,瞭解本質後二語可互換),並構建新內核 。組態與構建不完全一樣,因爲兩過程的輸入與產出不一樣(KEMIN:以2.6版爲例,組態的輸入是Kconfig,輸出是.config;.config是構建的輸出之一)。另外,由於kbuild具有很好的可擴展性,你可以很容易的把你的新內核 代碼,比如驅動程序,掛接入kbuild。

通過kbuild對內核 組態和編譯的過程可分爲四步:
第一,指定交叉開發環境(內核 的目標體系結構);
通常通過修改已有的makefile獲得,假設目標平臺是ARM,修改以下:

1. ARCH ?= arm

2. CROSS_COMPILE ?= arm-Linux -

或運行帶參數的make:

1. $ cd /usr/scr/Linux XX

2. $ make ARCH=arm CROSS_COMPILE=arm-Linux -

第二,內核 組件選擇
通過make XXconfig進行組件選擇,選擇組件的類型有:

* 處理器選擇
* 載板選擇
* 驅動選擇
* 通用內核 選項(generic kernel options)選擇
完成組件選擇後,kbuild會生成一些記錄文件(KEMIN:就是配置文件.config),以便後續步驟參考。

第三,編譯並鏈接
生成源碼的目標文件,並鏈接成單一內核 鏡像文件,此過程步驟以下:

1.make dep:生成頭文件依賴信息(.c文件依賴於哪個.h文件),此步只適用2.4版;
2.make clean/make mrpoper:make clean清理(先前)所有目標文件、內核 鏡像和所有中間生成文件,配置信息除外;make mrpoper則連配置信息也清除掉;
3.make:最後生成內核 鏡像——vmLinux 文件。內核 構建到處其實還沒有完,內核 鏡像還需要一些後處理(postprocessing),比如壓縮,比如加入啓動代碼(bootstrapping )等。由於平臺和bootloader的差異,後處理過程沒有標準。
第四,構建動態加載模塊
構建動態加載模塊,使用make modules 命令。

以上四步基本滿足一般的內核 構建需要,不過對[嵌入式系統 ],你可能需更多的自定義構建功能,比如:

你想爲你的BSP指一個單獨的目錄
你想手動修改配置信息,爲你的載板編譯所需的軟件
你想添加自己的鏈接器、編譯器和彙編參數
You may want to build intelligence in the kbuild for doing a systemwide build.
要想對內核 構建系統 做手腳,必須理解它的工作原理。

內核 配置子系統
內核 源碼目錄的頂層Makefile負責用來構建[內核 鏡像]和[動態加載模塊]二者。它通過遞歸源碼樹的子目錄來實現的,具體進入哪些子目錄取決於組件的選擇,也就是內核 配置。

配置什麼?
每種體系統 都會輸出組件列表給內核 配置時選擇,組件類型包括:

第一,處理器特性;
第二,硬件載板;
第三,載板特殊的硬件配置;
第四,內核系統 組件(這些組件或多或少是獨立於體系的,比如網絡協議棧)
配置庫
每種體系都關聯着一個組件數據庫,此庫以文件形式保存在arch/$ARCH目錄下。2.4版是config.in,2.6版是Kconfig。在配置內核 時,此文件會被解釋(parsed)來提供組件選擇。如果你要添加硬件特徵[配置項 ],你得修改此文件。

配置語言
雖然內核 配置使用make命令,[內核 配置子系統 ]的配置庫使用了一種不同的配置描述語言,並且2.4與2.6都有所不同。這種語言語法很簡單,很接近自然語言,這裏不詳述,只談使用技術。

什麼是[內核 配置子系統 ]?爲什麼可以使用不同的腳本語言?
因爲配置過程是整個內核 構建過程的串行子部分,產出特定的配置信息,所以完全可以使用獨立的更簡單的領域特定語言(domain-specific language)。

第一,每一個內核 子部分(subsection)都有單獨的配置文件定義配置規則,比如,網絡部分,配置信息保存在子目錄下的Kconfig。體系相關的配置文件會導入這個文件,例如,在2.4版,MIPS的體系配置文件(arch/mips/config- shared.in)有一行用來導入VFS的配置(fs/config.in)配置規則。

第二,配置文件.config通過名值對(name=value )保存[配置項 ]。配置項的名有前綴CONFIG_,後面跟着定義在配置文件裏的組件名。配置項的值有如下幾種:

布爾值:y or n
三態值(tristate): y, n, or m(module)
字符串:
整型:
十六進制數值:
第三,配置變量可以被定義爲是否需要用戶指定,如果不需要,配置變量使用默認值;

第四,可以爲配置變量定義依賴;依賴性用來決定配置項的可見性;

第五,每個配置變量都關聯一個幫助文本。

那麼[內核 配置子系統 ]是如何將被選擇的組件信息輸出到kbuild的呢?[內核 配置子系統 ]在用戶完全配置操作後會生成一個配置文件.config,內有已選定組件的[名值對]列表。頂層的makefile通過包含.config來達到組件選擇。

配置例子
讓我們看一個具體例子。假設我們有一個驅動源碼 drivers/net/sample.c,它的配置項是CONFIG_SAMPLE,布爾值。當使用命令make config進行配置時,用戶會被詢問:

Build sample network driver (CONFIG_SAMPLE) [y/N]?

如果他選擇y,那麼“CONFIG_SAMPLE=y ”會被添加到.config文件。在drivers/net/Makefile內有一行:

obj-$(CONFIG_SAMPLE)+= sample.o

當這個makefile被kbuild遞歸讀取到時,這一行會被翻譯成:

obj-y+= sample.o

obj-y 是kbuild預先定義好的構建規則。如果配置變量是三態值,配置選擇了模塊,那麼構建命令(makefile規則)是:

obj-m+= sample.o

對源碼進行配置
除了[makefile規則]需要[配置信息]進行動態生成外,內核 源碼內同樣有代碼依賴配置信息。比如2.4版內核 源碼init/main.c 有如下代碼:

#ifdef CONFIG_PCI

pci_init();

#endif

宏CONFIG_PCI定義與否的信息來自用戶配置操作。爲了把配置信息傳遞入源碼,kbuild 得把[名值對]翻譯成宏定義,保存在include/Linux /autoconf.h。然後,這個頭文件會被分拆爲多個頭文件,保存在include /config目錄。例如上面的例子,CONFIG_PCI生成一個include/config/pci.h與之對應,內有一行:

#define CONFIG_PCI

內核 添加代碼文件或代碼片斷
If you are a kernel developer and you make an addition to a particular subsystem, you place your files or edits in a specific subdirectory and update the Makefile if necessary to incorporate your changes. If your code is embedded in a file that already existed, you can surround your code within an #ifdef(CONFIG_<NAME>) block. If this value is selected in the .config file, it is #defined in include/ Linux /autoconf.h and your changes are included at compile time.

2.6版kbuild
在2.6版的kbuild,由於更多的[構建信息]被轉移到幾個特別的Makefile去,比如編譯器和庫信息定義在頂層Makefile和體系相關Makefile,構建規則集中定義在scripts/Makefile.*s,所以子目錄的Makefile的內容和格式變得相當簡潔,它們只需要編織以下三個字符串:

$(obj-y) 列出將鏈接入 built-in.o 的目標文件
$(obj-m) 列出將用以構建動態加載模塊的目標文件
$(lib-y) 列出將用以構建lib.a的目標文件
與2.4相比,2.6有如下一些改進:

第一,沒有需要顯式包含的Rules.make,構建規則隱式導出(KEMIN:?);
第二,每一個Makefile不指定目標,因爲有一個一致的構建目標:built-in.o;
第三,沒有subdirs-* 變量,目錄訪問列表與目標列表使用同一個變量:obj-*;
第四,目標文件有導出符號的不需要特別指定(kbuild會根據源碼內的EXPORT_SYMBOL宏進行判斷符號的導出)。
2.4版與2.6版內核 構建系統 的差別

第一,2.6版的內核 構建系統 使用不同的框架,2.6版的kbuild更簡單。比如,2.4版的體系相關makefile沒有任何的標準,2.6的有。
第二,2.6版的操作提示更友好;
第三,2.6版可以把編譯目標文件輸出到別處(通過make O=dir ),2.4只能原地編譯;
第四,2.4版的make dep會修改源文件的時間截,這會對一些源碼管理系統 產生影響。2.6版編譯內核 時不改動源碼文件,這樣保證你有一份只讀的[源碼樹]。當多個開發者都擁自己的[目標樹]的時候可以省下不少空間。
在正式開始遞歸調用make之前,kbuild必須得到一些信息,做一些預備工作,包括更新include/Linux /version.h和設定 include/asm的符號鏈接(指定體系相關的文件),此外,kbuild還要創建include/Linux /autoconf.h and include/Linux /config。完成後,kbuild開始下行訪問所有子目錄,分別調用它們的Makefile,生成它們各的目標文件。具體實現原理,請看下一小節。

2.6版kbuild實現原理(待譯)
In the source tree, virtually every directory has a Makefile. As mentioned in the previous section, the Makefiles in subtrees devoted to a particular category of the source code (or kernel subsystem) are fairly straightforward and merely define target source files to be added to the list that is then looked at to build them. Alongside these, five other Makefiles define rules and execute them. These include the source

root Makefile,
the arch/$(ARCH)/Makefile,
scripts/Makefile.build,
scripts/Makefile.clean,
scripts/Makefile.
Figure 9.7 shows the relationship between the various Makefiles. We define the relationships to be of the " include " type or of the " execute " type. When we refer to an "include" type relationship, we mean that the Makefile pulls in the information from a file by using the rule include <filename> . When we refer to an "execute" type relationship, we mean that the original Makefile executes a make f call to the secondary Makefile.

Linux內核構建系統原理
  
When we issue a make call at the root of the source tree, we call on the root Makefile. The root Makefile defines variables that are then exported to other Makefiles and issues further make calls in each of the root-level source subdirectories, passing off execution to them.

Calls to the compiler and linker are defined in scripts/Makefile.build. This means that when we descend into subdirectories and build the object by means of a call to make, we are somehow executing a rule defined in Makefile.build . This is done by way of the shorthand call $(Q) $(MAKE) $(build)=<dir>. This rule is the way make is invoked in each subdirectory. The build variable is shorthand for

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

A call to $(Q) $(MAKE) $(build)=fs expands to

"@ make f /path/to/source/scripts/Makefile.build bj=fs".

The scripts/Makefile.build then reads the Makefile of the directory it was passed as parameter (fs, in our example). This sub-Makefile has defined one or more of the lists obj-y, obj-m, lib-y, and others. The file scripts/Makefile.build, along with any definitions from the included scripts/ Makefile.lib, compiles the source files in the subdirectory and descends into any further subdirectories defined in the lists mentioned. The call is the same as what was just described.

Let's see how this works in an example. If, under the configuration tool, we go to the File Systems menu and select Ext3 journalling filesystem support, CONFIG_EXT3_FS will be set to y in the .config file. A snippet of the sub-Makefile corresponding to fs is shown here:

Makefile
49 obj-$(CONFIG_EXT3_FS) += ext3/
-----------------------------------------------------------------------

When make runs through this rule, it evaluates to obj-y += ext3/, making ext3/ one of the elements of obj-y. make, having recognized that this is a subdirectory, calls $(Q) $(MAKE) $(build)=ext3.

$(Q)

The $(Q) variable prefixes all $(MAKE) calls. With the 2.6 kernel tree and the cleanup of the kbuild infrastructure, you can suppress查禁; 壓制; 廢止 the verbose詳細的, 冗長的 mode of the make output. make prints the command line prior to executing it. When a line is prefixed with the @, the output (or echo) of that line is suppressed:
--------------------------------------------------------------------
Makefile
254 ifeq ($(KBUILD_VERBOSE),1)
255 quiet =
256 Q =
257 else
258 quiet=quiet_
259 Q = @
260 endif
--------------------------------------------------------------------

As we can see in these lines, Q is set to @ if KBUILD_VERBOSE is set to 0, which means that we do not want the compile to be verbose

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