1、概述linux
本文基於Linux Kernel 4.10版本講解。函數
Linux內核採用相似於GNU Make的kbuild構建而成,關於內核的構建系統kbuild,能夠先看本公衆號內之前的文章:ui
arm linux 內核的構建分爲三次編譯連接,一次組合。三次連接的中間結果分別是:spa
1.arch/arm/boot/compressed/vmlinux.net
2.arch/arm/boot/vmlinux.binip
3.arch/arm/boot/setup.binci
最後的組合就是將vmlinux.bin和setup.bin組合成arch/arm/boot/zImage:字符串
2、vmlinux的構建get
vmlinux的構建在頂層的Makefile中:cmd
cmd_link-vmlinux = \
$(CONFIG_SHELL) $<$(LD) $(LDFLAGS) $(LDFLAGS_vmlinux) ; \
$(if $(ARCH_POSTLINK), $(MAKE) -f$(ARCH_POSTLINK) $@, true)
vmlinux: scripts/link-vmlinux.sh vmlinux_prereq$(vmlinux-deps) FORCE
+$(callif_changed,link-vmlinux)
其中,call是make的內置函數,用於調用用戶本身定義的帶有參數的函數,這裏調用的是if_changed,參數是link-vmlinux。
if_changed是scripts/Kbuild.include裏定義的一個函數,定義以下:
if_changed = $(if $(strip $(any-prereq) $(arg-check)), \
@set -e; \
$(echo-cmd) $(cmd_$(1)); \
echo 'cmd_$@ :=$(make-cmd)' > $(dot-target).cmd)
any-prereq檢查是否有依賴比目標新,或者依賴尚未建立;arg-check檢查編譯目標的命令相對上次是否發生變化。set –e 命令表示make出錯時直接退出,加個@符號表示不顯示該set命令。cmd_$(1)中的1表示傳給if_changed的第一個參數。嵌入式物聯網智能硬件企鵝意義氣嗚嗚吧久零就易,在這裏傳給if_changed的實參是link-vmlinux,因此cmd_$(1)展開後爲cmd_link-vmlinux。
注意cmd_link-vmlinux中的$<表示規則中的第一個依賴,即scripts/link-vmlinux.sh。這個腳本用於vmlinux的連接,內容以下:
# Link of vmlinux
# ${1} - optionalextra .o files
# ${2} - output file
vmlinux_link()
{
locallds="${objtree}/${KBUILD_LDS}"
local objects
if [ "${SRCARCH}" !="um" ]; then
if [ -n"${CONFIG_THIN_ARCHIVES}" ]; then
objects="--whole-archivebuilt-in.o ${1}"
else
objects="${KBUILD_VMLINUX_INIT} \
--start-group \
${KBUILD_VMLINUX_MAIN} \
--end-group \
${1}"
fi
${LD} ${LDFLAGS}${LDFLAGS_vmlinux} -o ${2} \
-T ${lds} ${objects}
else
... ...
fi
}
若是平臺不是「um」,就將變量KBUILD_VMLINUX_INIT和KBUILD_VMLINUX_MAIN中的目標文件連接爲vmlinux;不然就直接編譯爲vmlinux,也就是式中的${2}。
接下來以core-y來分析變量KBUILD_VMLINUX_MAIN:
Linux-4.10/Makefile:
exportKBUILD_VMLINUX_INIT := $(head-y) $(init-y)
exportKBUILD_VMLINUX_MAIN := $(core-y) $(libs-y) $(drivers-y) $(net-y) $(virt-y)
exportKBUILD_LDS :=arch/$(SRCARCH)/kernel/vmlinux.lds
… …
core-y := usr/
… …
core-y += kernel/ certs/ mm/ fs/ ipc/security/ crypto/ block/
… …
core-y := $(patsubst %/, %/built-in.o,$(core-y))
make 的內置函數patsubst用於查找模式匹配的字符串,並進行替換。在上面這句語句裏,就是將全部‘/’替換成‘/built-in.o’。所以core-y最終變爲:
core-y :=user/built-in.o kernel/built-in.o mm/built-in.o fs/built-in.o ipc/built-in.osecurity/ built-in.o crypto/ built-in.o block/ built-in.o
再看其餘幾個相似的賦值語句:
init-y := $(patsubst %/,%/built-in.o, $(init-y))
drivers-y :=$(patsubst %/, %/built-in.o, $(drivers-y))
net-y := $(patsubst %/,%/built-in.o, $(net-y))
libs-y1 := $(patsubst %/, %/lib.a,$(libs-y))
libs-y2 := $(patsubst %/, %/built-in.o,$(libs-y))
libs-y := $(libs-y1) $(libs-y2)
virt-y := $(patsubst %/,%/built-in.o, $(virt-y))
不難看出,vmlinux就是由這些目錄下的built-in.o和lib.a連接而成。
vmlinux的另外一個依賴是vmlinux-deps,其構建規則也在頂層Makefile中定義:
Linux-4.10/Makefile:
vmlinux-dirs := $(patsubst %/,%,$(filter %/, $(init-y)$(init-m) \
$(core-y) $(core-m) $(drivers-y)$(drivers-m) \
$(net-y) $(net-m) $(libs-y) $(libs-m)$(virt-y)))
vmlinux-deps :=$(KBUILD_LDS) $(KBUILD_VMLINUX_INIT) $(KBUILD_VMLINUX_MAIN)
… …
# The actual objectsare generated when descending,
# make sure noimplicit rule kicks in
$(sort$(vmlinux-deps)): $(vmlinux-dirs) ;
… …
$(vmlinux-dirs):prepare scripts
$(Q)$(MAKE) $(build)=$@
目標vmlinux-deps的構建規則下沒有命令可執行,只依賴於另一個目標vmlinux-dirs, 該變量的賦值語句裏的filter表示過濾掉不以‘/’結尾的字符串。而filter的這些輸入變量,如core-y,其子目錄都是以‘/’結尾。所以vmlinux-dirs是一個多目標規則,至關於:
init: prepare scripts
$(Q) $(MAKE) $(build) =$@
kernel: preparescripts
$(Q) $(MAKE) $(build) =$@
… …
規則中的命令展開爲:
Make –f script/Makefile.buildobj=$@
Make的自動變量$@表示規則的目標,這裏就是要構建的子目錄init,kernel等。
總結一下,kbuild依次構建Makefile中指定的子目錄,生成built-in.o、lib.a等文件,而後連接爲vmlinux。