Linux 2.6內核Makefile分析
[摘要] 由于Linux的獨特優(yōu)勢,使越來(lái)越多的企業(yè)和科研機構把目光轉向Linux的開(kāi)發(fā)和研究上。目前Linux最新的穩定內核版本為2.6.17,但是當今絕大部分對于Linux Makefile的介紹文章都是基于2.4內核的,可以說(shuō)關(guān)于2.6內核Makefile相關(guān)的文章鳳毛麟角,筆者抽時(shí)間完成了這篇分析文章,讓讀者迅速熟悉Linux最新Makefile體系,從而加深對內核的理解,同時(shí)也希望能對Linux在公司的推廣起到一定的推動(dòng)作用,算是拋磚引玉吧!
本文引用地址:http://dyxdggzs.com/article/257987.htm1 Makefile組織層次
Linux的Make體系由如下幾部分組成:
Ø 頂層Makefile
頂層Makefile通過(guò)讀取配置文件,遞歸編譯內核代碼樹(shù)的相關(guān)目錄,從而產(chǎn)生兩個(gè)重要的目標文件:vmlinux和模塊。
Ø 內核相關(guān)Makefile
位于arch/$(ARCH) 目錄下,為頂層Makefile提供與具體硬件體協(xié)結構相關(guān)的信息。
Ø 公共編譯規則定義文件。
包括Makefile.build 、Makefile.clean、Makefile.lib、Makefile.host等文件組成。這些文件位于scripts目錄中,定義了編譯需要的公共的規則和定義。
Ø 內核配置文件 .config
通過(guò)調用make menuconfig或者make xconfig命令,用戶(hù)可以選擇需要的配置來(lái)生成期望的目標文件。
Ø 其他Makefile
主要為整個(gè)Makefile體系提供各自模塊的目標文件定義,上層Makefile根據它所定義的目標來(lái)完成各自模塊的編譯。
2 Makefile的使用
在編譯內核之前,用戶(hù)必須首先完成必要的配置。Linux內核提供了數不勝數的功能,支持眾多的硬件體系結構,這就需要用戶(hù)對將要生成的內核進(jìn)行裁減。內核提供了多種不同的工具來(lái)簡(jiǎn)化內核的配置,最簡(jiǎn)單的一種是字符界面下命令行工具:
make config
這個(gè)工具會(huì )依次遍歷內核所有的配置項,要求用戶(hù)進(jìn)行逐項的選擇配置。這個(gè)工具會(huì )耗費用戶(hù)太多時(shí)間,除非萬(wàn)不得以(你的編譯主機不支持其他配置工具)一般不建議使用。
用戶(hù)還可以使用利用ncurse庫編制的圖形界面工具,這就是大名鼎鼎的:
make menuconfig
相信以前對2.4內核比較熟悉的用戶(hù)一定不會(huì )陌生。當然在2.6內核中提供了更漂亮和方便的基于X11的圖形配置工具:
make xconfig
當用戶(hù)使用這個(gè)工具對Linux內核進(jìn)行配置時(shí),界面下方會(huì )出現這個(gè)配置項相關(guān)的幫助信息和簡(jiǎn)單描述,當你對內核配置選項不太熟悉時(shí),建議你使用這個(gè)工具來(lái)進(jìn)行內核配置。
當用戶(hù)完成配置后,配置工具會(huì )自動(dòng)生成.config文件,它被保存在內核代碼樹(shù)的根目錄下。用戶(hù)可以很容易找到它,當然用戶(hù)也可以直接對這個(gè)文件進(jìn)行簡(jiǎn)單的修改。但是當你修改過(guò)配置文件之后,你必須通過(guò)下面的命令來(lái)驗證和更新配置:
make oldconfig
跟2.4版本的不同之處在于,用戶(hù)不需要顯示的調用make dep命令來(lái)生成依賴(lài)文件,內核會(huì )自動(dòng)維護代碼間的依賴(lài)關(guān)系。
當一切工作完成以后,用戶(hù)只需要簡(jiǎn)單鍵入make,剩下所有的工作makefile就會(huì )自動(dòng)替你完成了。
3 Makefile編譯流程
當用戶(hù)使用Linux的Makefile編譯內核版本時(shí),Makefile的編譯流程如下:
Ø 使用命令行或者圖形界面配置工具,對內核進(jìn)行裁減,生成.config配置文件
Ø 保存內核版本信息到 include/linux/version.h
Ø 產(chǎn)生符號鏈接 include/asm,指向實(shí)際目錄 include/asm-$(ARCH)
Ø 為最終目標文件的生成進(jìn)行必要的準備工作
Ø 遞歸進(jìn)入 /init 、/core、 /drivers、 /net、 /lib等目錄和其中的子目錄來(lái)編譯生成所有的目標文件
Ø 鏈接上述過(guò)程產(chǎn)生的目標文件生成vmlinux,vmlinux存放在內核代碼樹(shù)的根目錄下
Ø 最后根據 arch/$(ARCH)/Makefile文件定義的后期編譯的處理規則建立最終的映象bootimage,包括創(chuàng )建引導記錄、準備initrd映象和相關(guān)處理
4 Makefile關(guān)鍵規則和定義描述
1) 目標定義
目標定義是Makefile文件的核心部分,目標定義通知Makefile需要生成哪些目標文件、如何根據特殊的編譯選項鏈接目標文件,同時(shí)控制哪些子目錄要遞歸進(jìn)入進(jìn)行編譯。
這個(gè)例子Makefile文件位于/fs/ext2目錄 :
#
# Makefile for the linux ext2-filesystem routines.
#
obj-$(CONFIG_EXT2_FS) += ext2.o
ext2-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o
ioctl.o namei.o super.o symlink.o
ext2-$(CONFIG_EXT2_FS_XATTR) += xattr.o xattr_user.o xattr_trusted.o
ext2-$(CONFIG_EXT2_FS_POSIX_ACL) += acl.o
ext2-$(CONFIG_EXT2_FS_SECURITY) += xattr_security.o
ext2-$(CONFIG_EXT2_FS_XIP) += xip.o
這表示與ext2相關(guān)的目標文件由 ext2-y定義的文件列表組成,其中ext2-$(*)是由內核配置文件.config中的配置項決定,最終Makefile會(huì )在這個(gè)目錄下統一生成一個(gè)目標文件ext2.o(由obj-$(CONFIG_EXT2_FS)決定)。其中obj-y表示為生成vmlinux文件所需要的目標文件集合,具體的文件依賴(lài)于內核配置。
Makefile會(huì )編譯所有的$(obj-y)中定義的文件,然后調用鏈接器將這些文件鏈接到built-in.o文件中。最終built-in.o文件通過(guò)頂層Makefile鏈接到vmlinux中。值得注意的是$(obj-y)的文件順序很重要。列表文件可以重復,文件第一次出現時(shí)將會(huì )鏈接到built-in.o中,后來(lái)出現的同名文件將會(huì )被忽略。文件順序直接決定了他們被調用的順序,這一點(diǎn)讀者需要特別注意。
讀者可能會(huì )在某些Makefile中發(fā)現lib-y定義,所有包含在lib-y定義中的目標文件都將會(huì )被編譯到該目錄下一個(gè)統一的庫文件中。值得注意的是lib-y定義一般被限制在 lib 和arch/$(ARCH)/lib 目錄中。
體系makefile文件和頂層makefile文件共同定義了如何建立vmlinux文件的規則。
$(head-y) 列舉首先鏈接到vmlinux的對象文件。
$(libs-y) 列舉了能夠找到lib.a文件的目錄。
其余的變量列舉了能夠找到內嵌對象文件的目錄。
$(init-y) 列舉的對象位于$(head-y)對象之后。
然后是如下位置順序:
$(core-y), $(libs-y), $(drivers-y) 和 $(net-y)。
頂層makefile定義了所有通用目錄,arch/$(ARCH)/Makefile文件只需增加體系相關(guān)的目錄。
例如: #arch/i386/Makefile
libs-y += arch/i386/lib/
core-y += arch/i386/kernel/
arch/i386/mm/
arch/i386/$(mcore-y)/
arch/i386/crypto/
drivers-$(CONFIG_MATH_EMULATION) += arch/i386/math-emu/
drivers-$(CONFIG_PCI) += arch/i386/pci/
…………………………………………
2) 目錄遞歸
Makefile文件只負責當前目錄下的目標文件,子目錄中的文件由子目錄中的makefile負責編譯,編譯系統使用obj-y 和 obj-m來(lái)自動(dòng)遞歸編譯各個(gè)子目錄中的文件。
對于fs/Makefile:
obj-$(CONFIG_EXT2_FS) += ext2/
如果在內核配置文件.config中,CONFIG_EXT2_FS被設置為y或者m,則內核makefile會(huì )自動(dòng)進(jìn)入ext2目錄來(lái)進(jìn)行編譯。內核Makefile只使用這些信息來(lái)決定是否需要編譯這個(gè)目錄,子目錄中的makefile規定哪些文件編譯為模塊,哪些文件編譯進(jìn)內核。
3) 依賴(lài)關(guān)系
Linux Makefile通過(guò)在編譯過(guò)程中生成的 .文件名.o.cmd(比如對于main.c文件,它對應的依賴(lài)文件名為.main.o.cmd)來(lái)定義相關(guān)的依賴(lài)關(guān)系。
一般文件的依賴(lài)關(guān)系由如下部分組成:
Ø 所有的前期依賴(lài)文件(包括所有相關(guān)的*.c 和 *.h)
Ø 所有與CONFIG_選項相關(guān)的文件
Ø 編譯目標文件所使用到的命令行
位于init目錄下的main.c文件的依賴(lài)文件.main.o.cmd內容如下,讀者可以結合起來(lái)理解上述文件依賴(lài)關(guān)系的三個(gè)組成部分:
cmd_init/main.o := gcc -m32 -Wp,-MD,init/.main.o.d -nostdinc -isystem /usr/lib/gcc-lib/i386-redhat-linux/3.2.2/include -D__KERNEL__ -Iinclude -Iinclude2 -I/home/linux/linux-2.6.17.11/include -include include/linux/autoconf.h -I/home/linux/linux-2.6.17.11/init -Iinit -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -Os -fomit-frame-pointer -pipe -msoft-float -mpreferred-stack-boundary=2 -march=i686 -mcpu=pentium4 -mregparm=3 -ffreestanding -I/home/linux/linux-2.6.17.11/include/asm-i386/mach-default -Iinclude/asm-i386/mach-default -DKBUILD_STR(s)=#s -DKBUILD_BASENAME=KBUILD_STR(main) -DKBUILD_MODNAME=KBUILD_STR(main) -c -o init/.tmp_main.o /home/linux/linux-2.6.17.11/init/main.c
deps_init/main.o :=
/home/linux/linux-2.6.17.11/init/main.c
$(wildcard include/config/x86/local/apic.h)
$(wildcard include/config/acpi.h)
# 由于篇幅的關(guān)系,此處略去一些定義
……………………………………..
include2/asm/mpspec_def.h
/home/linux/linux-2.6.17.11/include/asm-i386/mach-default/mach_mpspec.h
include2/asm/io_apic.h
include2/asm/apic.h
init/main.o: $(deps_init/main.o)
$(deps_init/main.o):
4) 特殊規則
特殊規則使用在內核編譯需要規則定義而沒(méi)有相應定義的時(shí)候。典型的例子如編譯時(shí)頭文件的產(chǎn)生規則。其他例子有體系makefile編譯引導映像的特殊規則。特殊規則寫(xiě)法同普通的makefile規則。
編譯程序在makefile所在的目錄不能被執行,因此所有的特殊規則需要提供前期文件和目標文件的相對路徑。
定義特殊規則時(shí)將使用到兩個(gè)變量:
$(src): $(src)是對于makefile文件目錄的相對路徑,當使用代碼樹(shù)中的文件時(shí)
使用該變量$(src)。
$(obj): $(obj)是目標文件目錄的相對路徑。生成文件使用$(obj)變量。
例如: #drivers/scsi/Makefile
$(obj)/53c8xx_d.h: $(src)/53c7,8xx.scr $(src)/script_asm.pl
$(CPP) -DCHIP=810 - $ | ... $(src)/script_asm.pl
這就是使用普通語(yǔ)法的特殊編譯規則。
目標文件依賴(lài)于兩個(gè)前提文件。目標文件的前綴是$(obj), 前提文件的前綴是
$(src)(因為它們不是生成文件)。
5) 引導映象
體系makefile文件定義了編譯vmlinux文件的目標對象,將它們壓縮和封裝成引導代碼,并復制到合適的位置。這包括各種安裝命令。在Linux中Makefile無(wú)法為所有的體系結構提供標準化的方法,因此常需要具體硬件體系結構下makefile提供附加處理規則。
附加處理過(guò)程常位于arch/$(ARCH)/下的boot/目錄。
內核編譯體系無(wú)法在boot/目錄下提供一種便捷的方法創(chuàng )建目標系統文件。因此arch/$(ARCH)/Makefile要調用make命令在boot/目錄下建立目標系統文件。建議使用的方法是在arch/$(ARCH)/Makefile中設置調用,并且使用完整路徑引用arch/$(ARCH)/boot/Makefile。
例如: #arch/i386/Makefile
boot := arch/i386/boot
bzImage: vmlinux
$(Q)$(MAKE) $(build)=$(boot) $(boot)/$@
建議使用$(Q)$(MAKE) $(build)=dir>方式在子目錄中調用make命令。
當執行不帶參數的make命令時(shí),將首先編譯第一個(gè)目標對象。在頂層makefile中第一個(gè)目標對象是all:。
一個(gè)體系結構需要定義一個(gè)默認的可引導映像。
增加新的前提文件給all目標可以設置不同于vmlinux的默認目標對象。
例如: #arch/i386/Makefile
all: bzImage
當執行不帶參數的make命令時(shí),bzImage文件將被編譯。
6) 常用編譯命令
if_changed
如果必要,執行傳遞的命令。
用法:
$(builtin-target): $(obj-y) FORCE
$(call if_changed,link_o_target)
當這條規則被使用時(shí)它將檢查哪些文件需要更新,或命令行被改變。后面這種情況將迫使
重新編譯編譯選項被改變的執行文件。使用if_changed的目標對象必須列舉在$( builtin-target)中,否則命令行檢查將失敗,目標一直會(huì )編譯。
if_changed_dep
如果必要,執行傳遞的命令并更新依賴(lài)文件。
用法:
%.o: %.S FORCE
$(call if_changed_dep,as_o_S)
當這條規則被使用時(shí)它將檢查哪些文件需要更新,或命令行被改變。同時(shí)它會(huì )重新檢測依賴(lài)關(guān)系的改變并將生成新的依賴(lài)文件。這是與if_changed命令的區別。
7) 定制命令
當正常執行帶編譯命令時(shí)命令的簡(jiǎn)短信息會(huì )被顯示(要想顯示詳細的命令,請在命令行中加入V=1)。要讓定制命令具有這種功能需要設置兩個(gè)變量:
quiet_cmd_command> - 將被顯示的內容
cmd_command> - 被執行的命令
例如: #
quiet_cmd_image = BUILD $@
cmd_image = $(obj)/tools/build $(BUILDFLAGS)
$(obj)/vmlinux.bin > $@
targets += bzImage
$(obj)/bzImage: $(obj)/vmlinux.bin $(obj)/tools/build FORCE
$(call if_changed,image)
@echo 'Kernel: $@ is ready'
執行make命令編譯$(obj)/bzImage目標時(shí)將顯示:
BUILD arch/i386/boot/bzImage
8) 預處理鏈接腳本
當編譯vmlinux映像時(shí)將使用arch/$(ARCH)/kernel/vmlinux.lds鏈接腳本。
相同目錄下的vmlinux.lds.S文件是這個(gè)腳本的預處理的變體。內核編譯系統知曉.lds
文件。并使用規則*lds.S -> *lds。
例如: #arch/i386/kernel/Makefile
always := vmlinux.lds
#Makefile
export CPPFLAGS_vmlinux.lds += -P -C -U$(ARCH)
$(always)賦值語(yǔ)句告訴編譯系統編譯目標是vmlinux.lds。$(CPPFLAGS_vmlinux.lds)
賦值語(yǔ)句告訴編譯系統編譯vmlinux.lds目標的編譯選項。
編譯*.lds時(shí)將使用到下面這些變量:
CPPFLAGS : 定義在頂層Makefile
EXTRA_CPPFLAGS : 可以設置在編譯的makefile文件中
CPPFLAGS_$(@F) : 目標編譯選項。注意要使用文件全名。
9) 主機輔助程序的編譯
內核編譯系統支持在編譯階段編譯主機可執行程序。為了使用主機程序需要兩個(gè)步驟:第一個(gè)步驟使用hostprogs-y變量告訴內核編譯系統有主機程序可用。第二步給主機程序添加潛在的依賴(lài)關(guān)系。有兩種方法,在規則中增加依賴(lài)關(guān)系或使用$(always)變量。這一部分的內容相對于其他內核文件的編譯要簡(jiǎn)單的多,感興趣的讀者可以參考scripts/Makefile.build中的相關(guān)內容。
10) Clean機制
clean命令清除在編譯內核生成的大部分文件,例如主機程序,列舉在 $(hostprogs-y)、$(hostprogs-m)、$(always)、$(extra-y)和$(targets)中目標文件都將被刪除。代碼目錄數中的*.[oas]、*.ko文件和一些由編譯系統產(chǎn)生的附加文件也將被刪除。
附加文件可以使用$(clean-files)進(jìn)行定義。
例如: #drivers/pci/Makefile
clean-files := devlist.h classlist.h
當執行make clean命令時(shí), devlist.h classlist.h兩個(gè)文件將被刪除。內核編譯系統默認這些文件與makefile具有相同的相對路徑,否則需要設置以'/'開(kāi)頭的絕對路徑。
刪除整個(gè)目錄使用以下方式:
例如: #scripts/package/Makefile
clean-dirs := $(objtree)/debian/
這樣就將刪除包括子目錄在內的整個(gè)debian目錄。如果不使用以'/'開(kāi)頭的絕對路徑內核編譯系統見(jiàn)默認使用相對路徑。
通常內核編譯系統根據obj-* := dir/進(jìn)入子目錄,但是在體系makefile中需要顯式使用如下方式:
例如: #arch/i386/boot/Makefile
subdir- := compressed/
上面賦值語(yǔ)句指示編譯系統執行make clean命令時(shí)進(jìn)入compressed/目錄。
在編譯最終的引導映像文件的makefile中有一個(gè)可選的目標對象名稱(chēng)是archclean。
例如: #arch/i386/Makefile
archclean:
$(Q)$(MAKE) $(clean)=arch/i386/boot
當執行make clean時(shí)編譯器進(jìn)入arch/i386/boot并象通常一樣工作。arch/i386/boot 中的makefile文件可以使用subdir-標識進(jìn)入更下層的目錄。
注意1: arch/$(ARCH)/Makefile不能使用subdir-,因為它被包含在頂層makefile文件中,在這個(gè)位置編譯機制是不起作用的。
注意2: 所有列舉在core-y、libs-y、drivers-y和net-y中的目錄將被make clean命令清除。
4 小結
隨著(zhù)Linux的飛速發(fā)展,越來(lái)越多的開(kāi)發(fā)人員將關(guān)注的焦點(diǎn)集中到Linux的研究和開(kāi)發(fā)上。如果想對Linux內核進(jìn)行研究和開(kāi)發(fā),就必須首先熟悉Linux 內核Makefile的組織和編譯過(guò)程。目前Linux最新的穩定內核版本為2.6.17,但是當今絕大部分對于Linux Makefile的介紹都是基于2.4內核的,可以說(shuō)關(guān)于2.6內核Makefile相關(guān)的文章鳳毛麟角,我特意抽時(shí)間完成了這篇分析文章,讓讀者迅速熟悉Linux最新Makefile體系,從而加深對內核的理解,同時(shí)也希望能對Linux在公司的推廣起到一定的推動(dòng)作用。
本文來(lái)自CSDN博客,http://blog.csdn.net/mbtrend/archive/2008/10/05/3016889.aspx
評論