linux配置、編譯內核實用工具
Make
Make是一種幫助大型軟件工程的編譯工作實現自動化的編程語言。正確地使用Make可以大大減少因編譯程序而花費的時間,因為它可以消除不必要的再編譯。Make的基本設計思想是如果目標文件是在最近一次對源文件的修改之后編譯的,它就是“新的”,不需要重新編譯;如果最近一次對源文件的修改之后沒有及時更新目標文件,那么該目標文件就是“舊的”,需要重新編譯。為了理解Make如何執(zhí)行一個任務,需要了解一些術語:
◆目標 需要執(zhí)行的一個任務。多數情況下它就是用戶要生成的文件的名字,但是它也可以僅是個任務的名字。
◆依賴關系 兩個目標之間相互依存的關系。如果修改目標B會造成目標A的修改,那么就說目標A依賴于目標B,B是A的先決條件。
◆變量 一種存儲臨時信息的載體。Make中使用的變量應該加上括號,例如$(TEMP)。
◆命令 執(zhí)行任務時使用的指令,可以是一條、多條,甚至沒有。
◆規(guī)則 一條完整的規(guī)則具有以下格式:
目標(target) : 先決條件(prerequisites)
規(guī)則(command)
......
其中只有目標必須要有,其它成分可以沒有。一條完整的規(guī)則描述了編譯一個目標的方法和依賴關系,是Makefile中最重要的部分。
◆Makefile文件 描述如何生成一個或多個目標的文件。它列出目標依賴的各個文件,并提供正確編譯這些目標所需要的規(guī)則。
接下來以2.4.23的kbuild為例,簡要介紹一下內核的構建過程。首先,完整的內核構建過程由以下五種Makefile封裝。
1.根目錄Makefile
它是最重要的Makefile,定義所有與體系結構無關的變量和目標。它讀取.config文件,并根據其信息最終生成vmlinux和modules。Make通過向下遞歸調用子目錄中的Makefile來編譯這兩個目標。
2.配置文件.config
執(zhí)行“make ”會在根目錄下生成該配置文件,其內容記錄了具體的配置選擇,也可以將舊內核的配置文件放在這里。
3.arch/*/Makefile
這是與特定體系結構相關的Makefile。它包含在根目錄下的Makefile中,為kbuild提供體系結構的特定信息。
4.子目錄Makefiles
它們存在于每個子目錄下,大約有幾百個。它們接受來自上層Make傳遞下來的信息,并根據這些信息來構造一個需要編譯的文件列表,并交由Rules.make處理。
5.Rules.make
幾乎每個子目錄Makefile都包含該Makefile。根據子目錄Makefiles構建的文件列表,Make使用Rules.make定義的通用規(guī)則來編譯所有來自列表的源文件。
kbuild的執(zhí)行過程是:Make從根目錄Makefile開始執(zhí)行,從中獲得與體系結構無關的變量和依賴關系,并同時從arch/*/Makefile中獲得體系特定的變量等信息,這些信息擴展了根目錄Makefile提供的變量。此時kbuild已經擁有構建內核需要的所有變量和目標。然后,Make進入子目錄,把部分變量傳遞給子目錄Makefile。子目錄Makefile根據配置信息決定編譯哪些源文件,從而構建出一個需要編譯的文件列表。最后,Rules.make根據其定義的編譯規(guī)則決定這些文件的編譯方式。
需要注意的是,由于Make的向下遞歸特性和無序性,其執(zhí)行過程并不完全遵守順序逐行執(zhí)行的規(guī)則,但無論Make的執(zhí)行有多復雜,也只分為兩個階段。第一個階段Make會讀取所有變量和分析所有目標的依賴關系,并最終建立一棵依賴關系樹。同時,所有的立即型變量(通過“:=”賦值)在這個過程中被擴展,就像C變量一樣。而在這個階段的最后,所有的延遲型變量才被擴展(通過“=”賦值)。這點需要格外注意。第二個階段Make會根據依賴關系樹執(zhí)行命令。
因此,一個目標和其先決條件的規(guī)則定義的順序是無所謂的,很可能一個目標的先決條件的規(guī)則定義在百行以后才出現。Make會耐心讀完所有的Makefile后分析得出依賴關系樹。
GCC
GCC是GNU的免費編譯程序,也是內核惟一指定使用的編譯器。GCC在執(zhí)行一個完整的編譯任務時會經過以下步驟:
◆預處理 GCC會調用cpp程序來分析各種宏指令,如#define、#if、#include等。
◆編譯 這一階段根據輸入文件產生匯編語言指令。由于通常情況下是立即調用匯編程序as,所以輸出一般不保存在文件中,可以使用-S選項強制輸出源程序的匯編版本。
◆匯編 這一階段將匯編語言源程序作為輸入,生成.o目標文件。
◆鏈接 這是最后一個階段。該階段中,各個.o模塊被鏈接在一起構成可執(zhí)行文件。
as
用戶可以明確地要求使用as來直接處理匯編文件。as產生的目標文件可以分為文本段(.text)、數據段(.data)和未初始化數據段(.bss)。
ld
與as相似,用戶可以明確地要求使用ld鏈接程序將幾個模塊組合成一個單獨的可執(zhí)行文件。其鏈接過程通常由一個叫l(wèi)d鏈接腳本的文件來描述。該腳本使用Linker Command Language編寫。使用“l(fā)d --verose”命令可以看到這個默認使用的ld鏈接腳本。
ar
ar是GNU的二進制文件處理程序,用于創(chuàng)建、修改及從歸檔文件中抽取文件。由它生成的.a歸檔文件實際上是一個包含許多可執(zhí)行二進制代碼子程序集合的庫文件。
RPMBuild
使用“make rpm”可以把內核源代碼制作成RPM包。在此之前,kbuild會執(zhí)行“make spec”生成rpmbuild程序用到的spec文件,詳見“man rpmbuild”。
中間件
根目錄scripts下的各種腳本和C源文件都可以稱作中間件。它們并不是內核組件的一部分,只是在kbuild執(zhí)行過程中的輔助程序。以split-include為例,講述配置文件的運作機理。
.config由關鍵字/值對組成,其內容類似于:
CONFIG_MPENTIUMIII=y
# CONFIG_MPENTIUM4 is not set
CONFIG_REISERFS_FS=m
這些信息在執(zhí)行“make ”時自動生成。同時include/linux/autoconf.h依照.config的內容生成。它的格式類似于:
#define CONFIG_MPENTIUMIII 1
#undef CONFIG_MPENTIUM4
#undefCONFIG_REISERFS_FS
#define CONFIG_REISERFS_FS_MODULE 1
對比一下不難發(fā)現,include/linux/autoconf.h明確地洞悉了.config的意圖:哪些組件不編譯,哪些需要編譯進內核,而哪些又要作為模塊來編譯?split-include根據include/linux/autoconf.h在include/config/下建立相關的目錄和.h文件。每個.h文件只包括include/linux/autoconf.h中的某一行,比如在配置內核選項時支持NTFS文件系統,并把它編譯進內核,在.config中就會生成“CONFIG_NTFS_FS=y”,相應地在include/linux/autoconf.h中會生成“#define CONFIG_NTFS_FS 1”一項。這樣,所有與NTFS文件系統相關的C源文件都會包含include/config/ntfs/fs.h頭文件。
如果以前編譯過內核,并且沒有使用過“make mrproper”,.config、include/linux/autoconf.h和include/linux/config/就不會被刪除。這里涉及到新舊內核的配置問題。一個全新的內核代碼是未經配置的。如果只在原內核的功能基礎上增加對NTFS的支持,那么從頭開始配置無疑是浪費時間??梢岳^續(xù)使用原內核的.config文件,而所有的配置信息不會有任何更改,并且可以直接在原配置的基礎上增加新功能。
在復雜的情況下,保留的舊內核配置信息還要與新的配置信息進行比較:哪些舊信息需要覆蓋,哪些需要保留?下面來看一下幾種可能的情況:
舊值保存在include/config/下的.h文件中,新值保存在新生成的include/linux/autoconf.h文件中。split-include的代碼不僅描述了如何處理這五種情況,還描述了include/config/下文件和子目錄的生成過程。
評論