Linux 3.0核心Makefile分析
[摘要] 由於Linux的獨特優勢,使越來越多的企業和科研機構把目光轉向Linux的開發和研究上。目前Linux最新的穩定核心版本為2.6.17,但是當今絕大部分對於Linux Makefile的介紹文章都是基於2.4核心的,可以說關於2.6核心Makefile相關的文章鳳毛麟角,筆者抽時間完成了這篇分析文章,讓讀者迅速熟悉Linux最新Makefile體系,從而加深對核心的理解,同時也希望能對Linux在公司的推廣起到一定的推動作用,算是拋磚引玉吧!
1 Makefile組織層次
Linux的Make體系由如下幾部分組成:
Ø 頂層Makefile
頂層Makefile通過讀取配置檔案,遞迴編譯核心程式碼樹的相關目錄,從而產生兩個重要的目標檔案:vmlinux和模組。
Ø 核心相關Makefile
位於arch/$(ARCH) 目錄下,為頂層Makefile提供與具體硬體體協結構相關的資訊。
Ø 公共編譯規則定義檔案。
包括Makefile.build 、Makefile.clean、Makefile.lib、Makefile.host等檔案組成。這些檔案位於scripts目錄中,定義了編譯需要的公共的規則和定義。
Ø 核心配置檔案 .config
通過呼叫make menuconfig或者make xconfig命令,使用者可以選擇需要的配置來生成期望的目標檔案。
Ø 其他Makefile
主要為整個Makefile體系提供各自模組的目標檔案定義,上層Makefile根據它所定義的目標來完成各自模組的編譯。
2 Makefile的使用
在編譯核心之前,使用者必須首先完成必要的配置。Linux核心提供了數不勝數的功能,支援眾多的硬體體系結構,這就需要使用者對將要生成的核心進行裁減。核心提供了多種不同的工具來簡化核心的配置,最簡單的一種是字元介面下命令列工具:
make config
這個工具會依次遍歷核心所有的配置項,要求使用者進行逐項的選擇配置。這個工具會耗費使用者太多時間,除非萬不得以(你的編譯主機不支援其他配置工具)一般不建議使用。
使用者還可以使用利用ncurse庫編制的圖形介面工具,這就是大名鼎鼎的:
make menuconfig
相信以前對2.4核心比較熟悉的使用者一定不會陌生。當然在2.6核心中提供了更漂亮和方便的基於X11的圖形配置工具:
make xconfig
當用戶使用這個工具對Linux核心進行配置時,介面下方會出現這個配置項相關的幫助資訊和簡單描述,當你對核心配置選項不太熟悉時,建議你使用這個工具來進行核心配置。
當用戶完成配置後,配置工具會自動生成.config檔案,它被儲存在核心程式碼樹的根目錄下。使用者可以很容易找到它,當然使用者也可以直接對這個檔案進行簡單的修改。但是當你修改過配置檔案之後,你必須通過下面的命令來驗證和更新配置:
make oldconfig
跟2.4版本的不同之處在於,使用者不需要顯示的呼叫make dep命令來生成依賴檔案,核心會自動維護程式碼間的依賴關係。
當一切工作完成以後,使用者只需要簡單鍵入make,剩下所有的工作makefile就會自動替你完成了。
3 Makefile編譯流程
當用戶使用Linux的Makefile編譯核心版本時,Makefile的編譯流程如下:
Ø 使用命令列或者圖形介面配置工具,對核心進行裁減,生成.config配置檔案
Ø 儲存核心版本資訊到 include/linux/version.h
Ø 產生符號連結 include/asm,指向實際目錄 include/asm-$(ARCH)
Ø 為最終目標檔案的生成進行必要的準備工作
Ø 遞迴進入 /init 、/core、 /drivers、 /net、 /lib等目錄和其中的子目錄來編譯生成所有的目標檔案
Ø 連結上述過程產生的目標檔案生成vmlinux,vmlinux存放在核心程式碼樹的根目錄下
Ø 最後根據 arch/$(ARCH)/Makefile檔案定義的後期編譯的處理規則建立最終的映象bootimage,包括建立引導記錄、準備initrd映象和相關處理
4 Makefile關鍵規則和定義描述
1) 目標定義
目標定義是Makefile檔案的核心部分,目標定義通知Makefile需要生成哪些目標檔案、如何根據特殊的編譯選項鍊接目標檔案,同時控制哪些子目錄要遞迴進入進行編譯。
這個例子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相關的目標檔案由 ext2-y定義的檔案列表組成,其中ext2-$(*)是由核心配置檔案.config中的配置項決定,最終Makefile會在這個目錄下統一生成一個目標檔案ext2.o(由obj-$(CONFIG_EXT2_FS)決定)。其中obj-y表示為生成vmlinux檔案所需要的目標檔案集合,具體的檔案依賴於核心配置。
Makefile會編譯所有的$(obj-y)中定義的檔案,然後呼叫連結器將這些檔案連結到built-in.o檔案中。最終built-in.o檔案通過頂層Makefile連結到vmlinux中。值得注意的是$(obj-y)的檔案順序很重要。列表檔案可以重複,檔案第一次出現時將會連結到built-in.o中,後來出現的同名檔案將會被忽略。檔案順序直接決定了他們被呼叫的順序,這一點讀者需要特別注意。
讀者可能會在某些Makefile中發現lib-y定義,所有包含在lib-y定義中的目標檔案都將會被編譯到該目錄下一個統一的庫檔案中。值得注意的是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檔案只需增加體系相關的目錄。
例如: #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來自動遞迴編譯各個子目錄中的檔案。
對於fs/Makefile:
obj-$(CONFIG_EXT2_FS) += ext2/
如果在核心配置檔案.config中,CONFIG_EXT2_FS被設定為y或者m,則核心makefile會自動進入ext2目錄來進行編譯。核心Makefile只使用這些資訊來決定是否需要編譯這個目錄,子目錄中的makefile規定哪些檔案編譯為模組,哪些檔案編譯進核心。
3) 依賴關係
Linux Makefile通過在編譯過程中生成的 .檔名.o.cmd(比如對於main.c檔案,它對應的依賴檔名為.main.o.cmd)來定義相關的依賴關係。
一般檔案的依賴關係由如下部分組成:
Ø 所有的前期依賴檔案(包括所有相關的*.c 和 *.h)
Ø 所有與CONFIG_選項相關的檔案
Ø 編譯目標檔案所使用到的命令列
位於init目錄下的main.c檔案的依賴檔案.main.o.cmd內容如下,讀者可以結合起來理解上述檔案依賴關係的三個組成部分:
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 -D"KBUILD_STR(s)=/#s" -D"KBUILD_BASENAME=KBUILD_STR(main)" -D"KBUILD_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) /
# 由於篇幅的關係,此處略去一些定義
……………………………………..
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) 特殊規則
特殊規則使用在核心編譯需要規則定義而沒有相應定義的時候。典型的例子如編譯時標頭檔案的產生規則。其他例子有體系makefile編譯引導映像的特殊規則。特殊規則寫法同普通的makefile規則。
編譯程式在makefile所在的目錄不能被執行,因此所有的特殊規則需要提供前期檔案和目標檔案的相對路徑。
定義特殊規則時將使用到兩個變數:
$(src): $(src)是對於makefile檔案目錄的相對路徑,當使用程式碼樹中的檔案時
使用該變數$(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
這就是使用普通語法的特殊編譯規則。
目標檔案依賴於兩個前提檔案。目標檔案的字首是$(obj), 前提檔案的字首是
$(src)(因為它們不是生成檔案)。
5) 引導映象
體系makefile檔案定義了編譯vmlinux檔案的目標物件,將它們壓縮和封裝成引導程式碼,並複製到合適的位置。這包括各種安裝命令。在Linux中Makefile無法為所有的體系結構提供標準化的方法,因此常需要具體硬體體系結構下makefile提供附加處理規則。
附加處理過程常位於arch/$(ARCH)/下的boot/目錄。
核心編譯體系無法在boot/目錄下提供一種便捷的方法建立目標系統檔案。因此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)/[email protected]
建議使用"$(Q)$(MAKE) $(build)=<dir>"方式在子目錄中呼叫make命令。
當執行不帶引數的make命令時,將首先編譯第一個目標物件。在頂層makefile中第一個目標物件是all:。
一個體繫結構需要定義一個預設的可引導映像。
增加新的前提檔案給all目標可以設定不同於vmlinux的預設目標物件。
例如: #arch/i386/Makefile
all: bzImage
當執行不帶引數的"make"命令時,bzImage檔案將被編譯。
6) 常用編譯命令
if_changed
如果必要,執行傳遞的命令。
用法:
$(builtin-target): $(obj-y) FORCE
$(call if_changed,link_o_target)
當這條規則被使用時它將檢查哪些檔案需要更新,或命令列被改變。後面這種情況將迫使
重新編譯編譯選項被改變的執行檔案。使用if_changed的目標物件必須列舉在$( builtin-target)中,否則命令列檢查將失敗,目標一直會編譯。
if_changed_dep
如果必要,執行傳遞的命令並更新依賴檔案。
用法:
%.o: %.S FORCE
$(call if_changed_dep,as_o_S)
當這條規則被使用時它將檢查哪些檔案需要更新,或命令列被改變。同時它會重新檢測依賴關係的改變並將生成新的依賴檔案。這是與if_changed命令的區別。
7) 定製命令
當正常執行帶編譯命令時命令的簡簡訊息會被顯示(要想顯示詳細的命令,請在命令列中加入V=1)。要讓定製命令具有這種功能需要設定兩個變數:
quiet_cmd_<command> - 將被顯示的內容
cmd_<command> - 被執行的命令
例如: #
quiet_cmd_image = BUILD [email protected]
cmd_image = $(obj)/tools/build $(BUILDFLAGS) /
$(obj)/vmlinux.bin > [email protected]
targets += bzImage
$(obj)/bzImage: $(obj)/vmlinux.bin $(obj)/tools/build FORCE
$(call if_changed,image)
@echo 'Kernel: [email protected] is ready'
執行make命令編譯$(obj)/bzImage目標時將顯示:
BUILD arch/i386/boot/bzImage
8) 預處理連結指令碼
當編譯vmlinux映像時將使用arch/$(ARCH)/kernel/vmlinux.lds連結指令碼。
相同目錄下的vmlinux.lds.S檔案是這個指令碼的預處理的變體。核心編譯系統知曉.lds
檔案。並使用規則*lds.S -> *lds。
例如: #arch/i386/kernel/Makefile
always := vmlinux.lds
#Makefile
export CPPFLAGS_vmlinux.lds += -P -C -U$(ARCH)
$(always)賦值語句告訴編譯系統編譯目標是vmlinux.lds。$(CPPFLAGS_vmlinux.lds)
賦值語句告訴編譯系統編譯vmlinux.lds目標的編譯選項。
編譯*.lds時將使用到下面這些變數:
CPPFLAGS : 定義在頂層Makefile
EXTRA_CPPFLAGS : 可以設定在編譯的makefile檔案中
CPPFLAGS_$(@F) : 目標編譯選項。注意要使用檔案全名。
9) 主機輔助程式的編譯
核心編譯系統支援在編譯階段編譯主機可執行程式。為了使用主機程式需要兩個步驟:第一個步驟使用hostprogs-y變數告訴核心編譯系統有主機程式可用。第二步給主機程式新增潛在的依賴關係。有兩種方法,在規則中增加依賴關係或使用$(always)變數。這一部分的內容相對於其他核心檔案的編譯要簡單的多,感興趣的讀者可以參考scripts/Makefile.build中的相關內容。
10) Clean機制
clean命令清除在編譯核心生成的大部分檔案,例如主機程式,列舉在 $(hostprogs-y)、$(hostprogs-m)、$(always)、$(extra-y)和$(targets)中目標檔案都將被刪除。程式碼目錄數中的"*.[oas]"、"*.ko"檔案和一些由編譯系統產生的附加檔案也將被刪除。
附加檔案可以使用$(clean-files)進行定義。
例如: #drivers/pci/Makefile
clean-files := devlist.h classlist.h
當執行"make clean"命令時, "devlist.h classlist.h"兩個檔案將被刪除。核心編譯系統預設這些檔案與makefile具有相同的相對路徑,否則需要設定以'/'開頭的絕對路徑。
刪除整個目錄使用以下方式:
例如: #scripts/package/Makefile
clean-dirs := $(objtree)/debian/
這樣就將刪除包括子目錄在內的整個debian目錄。如果不使用以'/'開頭的絕對路徑核心編譯系統見預設使用相對路徑。
通常核心編譯系統根據"obj-* := dir/"進入子目錄,但是在體系makefile中需要顯式使用如下方式:
例如: #arch/i386/boot/Makefile
subdir- := compressed/
上面賦值語句指示編譯系統執行"make clean"命令時進入compressed/目錄。
在編譯最終的引導映像檔案的makefile中有一個可選的目標物件名稱是archclean。
例如: #arch/i386/Makefile
archclean:
$(Q)$(MAKE) $(clean)=arch/i386/boot
當執行"make clean"時編譯器進入arch/i386/boot並象通常一樣工作。arch/i386/boot 中的makefile檔案可以使用subdir-標識進入更下層的目錄。
注意1: arch/$(ARCH)/Makefile不能使用"subdir-",因為它被包含在頂層makefile檔案中,在這個位置編譯機制是不起作用的。
注意2: 所有列舉在core-y、libs-y、drivers-y和net-y中的目錄將被"make clean"命令清除。
4 小結
隨著Linux的飛速發展,越來越多的開發人員將關注的焦點集中到Linux的研究和開發上。如果想對Linux核心進行研究和開發,就必須首先熟悉Linux 核心Makefile的組織和編譯過程。目前Linux最新的穩定核心版本為2.6.17,但是當今絕大部分對於Linux Makefile的介紹都是基於2.4核心的,可以說關於2.6核心Makefile相關的文章鳳毛麟角,我特意抽時間完成了這篇分析文章,讓讀者迅速熟悉Linux最新Makefile體系,從而加深對核心的理解,同時也希望能對Linux在公司的推廣起到一定的推動作用。
5 參考資料
相關推薦
Linux 3.0核心Makefile分析
[摘要] 由於Linux的獨特優勢,使越來越多的企業和科研機構把目光轉向Linux的開發和研究上。目前Linux最新的穩定核心版本為2.6.17,但是當今絕大部分對於Linux Makefile的介紹文章都是基於2.4核心的,可以說關於2.6核心Makefile相
Linux核心原始碼分析--zImage出生實錄(Linux-3.0 ARMv7)
此文為兩年前為好友劉慶敏的書《嵌入式Linux開發詳解--基於AT91RM9200和Linux 2.6》中幫忙寫的章節的重新整理。如有雷同,純屬必然。經作者同意,將我寫的部分重新整理後放入blog中。 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
LInux-3.0.8中基於S5PV210的GPIO模塊代碼追蹤和分析
clas deb down then rect drivers 基於 lee 使用 編寫按鍵驅動時,想知道內核是如何管理GPIO的,所以開始追蹤代碼,中間走了一些彎路,現記錄於此。 追蹤代碼之前,我猜測:第一,這部分代碼應該在系統set up階段執行;第二,GPIO
LInux-3.0.8中基於S5PV210的IRQ模塊代碼追蹤和分析
PV 塊代碼 smd 函數定義 void 全局 對數 radix ali init/main.c: 1 asmlinkage void start_kernel(void) 2 { 3 ...... 4 early_irq_init(); 5 in
linux 3.0.8 alsa資料流程分析
ALSA開啟資料流程 soc_pcm_open => cpu_dai->driver->ops->startup => platform->driver->ops->open => co
Linux裝置驅動開發詳解 第3版 (即 Linux裝置驅動開發詳解 基於最新的Linux 4 0核心 )進展同步更
本博實時更新《Linux裝置驅動開發詳解(第3版)》的最新進展。 目前已經完成稿件。 2015
基於fl2440核心linux-3.0移植----新增adc驅動
三、配置核心 make menuconfig來配置核心,因為我用的核心是linux-3.0版本,其對ADC是預設選項的(不可選擇), System Type ---> -*- ADC common driver support 如果用的核心版本是不可選擇的,那個可以直接建立裝置
Linux Kernel 3.10核心原始碼分析--塊裝置層request plug/unplug機制
一、基本原理Linux塊裝置層使用了plug/unplug(蓄流/洩流)的機制來提升IO吞吐量。基本原理為:當IO請求提交時,不知直接提交給底層驅動,而是先將其放入一個佇列中(相當於水池),待一定時機或週期後再將該佇列中的請求統一下發。將請求放入佇列的過程即plug(蓄流)
Linux-3.0.8 input subsystem代碼閱讀筆記
wak evdev dump 輸入子系統 延遲 cbi reg 用戶空間 rup 先亂序記錄一下閱讀Linux input subsystem代碼的筆記。 在input device driver的入口代碼部分,需要分配並初始化input device結構,內
《linux裝置驅動開發》,基於最新的linux 4.0核心-----筆記
第二章 Linux 的核心結構及構建 ---->這一章是自己總結的 1、核心結構(主要是下面這幾個部分) 系統呼叫介面<–>System call interface 程序管理<------>Process manag
Linux 3.15核心將啟用非同步執行緒來減少掛起和恢復時間
Phoronix近期報道:Linux 3.15核心版本,預計將於2014年中期公佈,該版本“將有大量的ACPI和電源管理的更新”,並允許基於Linux的計算機掛起和恢復速度更快。 Linux的最新的穩定版本是3.13.6版本,Linux 3.14即將釋出,同時Linux 3.15的合併視窗即將開啟。
iperf-2.0.5移植到IMX6DQRM的linux-3.0.35
首先匯出交叉編譯鏈的路徑: export PATH=$PATH:/opt/freescale/usr/local/gcc-4.6.2-glibc-2.13-linaro-multilib-2011.12/fsl-linaro-toolchain/bin/ 切換到iperf-2.0.5的主目錄
將Ubuntu 10.04自帶2.6.32核心升級成3.0核心圖文教程
1、完成如下實踐工作,並截圖說明實踐步驟:1)下載安裝虛擬機器VMWare Workstation;2)在虛擬機器中安裝Linux2.x ;3)通過自行編譯,將Linux 2.x核心升級到Linux 3.x。 1) 安裝VMware Workstation 11.1
Linux系統啟動那些事—基於Linux 3.10核心
對於嵌入式平臺ARM平臺,說說其NANDFlash的啟動過程,請先看圖2.2描述的NAND flash中的程式佈局,上電時,首先cpu會自動將自動從NAND flash中拷貝一定程式碼到記憶體中執行,這是任何支援nand方式啟動必須支援的,一般我見到的有2K還有4K的,這部分的程式碼我們將其稱為boots
【Linux 1.0核心原始碼剖析】執行程式——exec.c
父程序 fork的子程序的目的自然不是建立一個幾乎與自己一模一樣的程序。而是通過子程序呼叫 exec 函式簇去執行另外一個程式。exec() 系統呼叫必須定位該執行檔案的二進位制映像,載入並執行它。 exec() 的Linux實現支援不同的二進位制格式,這是通過 linux
ernel 3.10核心原始碼分析--KVM相關--虛擬機器執行
1、基本原理 KVM虛擬機器通過字元裝置/dev/kvm的ioctl介面建立和執行,相關原理見之前的文章說明。 虛擬機器的執行通過/dev/kvm裝置ioctl VCPU介面的KVM_RUN指令實現,在VM和VCPU建立好並完成初始化後,就可以排程該虛擬機器運行了,通
交叉編譯基於ARM架構的linux-3.6核心遇到的問題及解決方法
編譯linux-3.6核心: 安裝交叉編譯工具:arm-linux-gnueabi-gcc 下載並解壓linux-3.6原始碼 清楚原始碼中的無用的編譯檔案:makeclean清除除了config之外的所有編譯出來的文件,makemrproper將配置的config也一併清
iMX6Q Linux 3.0.35移植ffmpeg及二進位制下載
編譯環境:Ubuntu 16.04.4交叉編譯工具:gcc-4.6.2-glibc-2.13-linaro-multilib-2011.12 FFmpeg是一套可以用來記錄、轉換數字音訊、視訊,並能將其轉化為流的開源計算機程式。採用LGPL或GPL許可證。它提供
關於召回《Linux裝置驅動開發詳解-基於最新的Linux 4.0核心》的通知
問題描述關於《Linux裝置驅動開發詳解:基於最新的Linux 4.0核心》一書1.華章分社在沒
QEMU1.3.0的原始碼分析一 : 原始碼目錄簡介
最近在研究QEMU,讀了一些QEMU的原始碼,因為涉及的東西比較多,找到的資料又都比較破碎,不太完整。所以將最近的成果總結一下。相比其他的開源軟體來說,QEMU原始碼下面目錄比較多,下面就先把這些目錄的內容大致整理一下。docs/ 包含了一些文件,說實話,對初學者來說,讀這