天天看點

你知道 Linux 核心是如何建構的嗎?

讓我們開始吧!

你知道 Linux 核心是如何建構的嗎?

(題圖來自:adafruit.com)

<a target="_blank"></a>

在開始編譯前要進行很多準備工作。最主要的就是找到并配置好配置檔案,<code>make</code> 指令要使用到的參數都需要從這些配置檔案擷取。現在就讓我們深入核心的根 <code>makefile</code> 吧

<code>version = 4</code>

<code>patchlevel = 2</code>

<code>sublevel = 0</code>

<code>extraversion = -rc3</code>

<code>name = hurr durr i'ma sheep</code>

這些變量決定了目前核心的版本,并且被使用在很多不同的地方,比如同一個 <code>makefile</code> 中的 <code>kernelversion</code>:

<code>kernelversion = $(version)$(if $(patchlevel),.$(patchlevel)$(if $(sublevel),.$(sublevel)))$(extraversion)</code>

接下來我們會看到很多<code>ifeq</code> 條件判斷語句,它們負責檢查傳遞給 <code>make</code> 的參數。核心的 <code>makefile</code> 提供了一個特殊的編譯選項 <code>make help</code> ,這個選項可以生成所有的可用目标和一些能傳給 <code>make</code> 的有效的指令行參數。舉個例子,<code>make v=1</code> 會在建構過程中輸出詳細的編譯資訊,第一個 <code>ifeq</code> 就是檢查傳遞給 make 的<code>v=n</code> 選項。

<code>ifeq ("$(origin v)", "command line")</code>

<code>kbuild_verbose = $(v)</code>

<code>endif</code>

<code>ifndef kbuild_verbose</code>

<code>kbuild_verbose = 0</code>

<code></code>

<code>ifeq ($(kbuild_verbose),1)</code>

<code>quiet =</code>

<code>q =</code>

<code>else</code>

<code>quiet=quiet_</code>

<code>q = @</code>

<code>export quiet q kbuild_verbose</code>

如果 <code>v=n</code> 這個選項傳給了 <code>make</code> ,系統就會給變量 <code>kbuild_verbose</code> 選項附上 <code>v</code> 的值,否則的話<code>kbuild_verbose</code> 就會為 <code>0</code>。然後系統會檢查 <code>kbuild_verbose</code> 的值,以此來決定 <code>quiet</code> 和<code>q</code> 的值。符号 <code>@</code> 控制指令的輸出,如果它被放在一個指令之前,這條指令的輸出将會是 <code>cc scripts/mod/empty.o</code>,而不是<code>compiling .... scripts/mod/empty.o</code>(lctt 譯注:cc 在 makefile 中一般都是編譯指令)。在這段最後,系統導出了所有的變量。

下一個 <code>ifeq</code> 語句檢查的是傳遞給 <code>make</code> 的選項 <code>o=/dir</code>,這個選項允許在指定的目錄 <code>dir</code> 輸出所有的結果檔案:

<code>ifeq ($(kbuild_src),)</code>

<code>ifeq ("$(origin o)", "command line")</code>

<code>kbuild_output := $(o)</code>

<code>ifneq ($(kbuild_output),)</code>

<code>saved-output := $(kbuild_output)</code>

<code>kbuild_output := $(shell mkdir -p $(kbuild_output) &amp;&amp; cd $(kbuild_output) \</code>

<code>&amp;&amp; /bin/pwd)</code>

<code>$(if $(kbuild_output),, \</code>

<code>$(error failed to create output directory "$(saved-output)"))</code>

<code>sub-make: force</code>

<code>$(q)$(make) -c $(kbuild_output) kbuild_src=$(curdir) \</code>

<code>-f $(curdir)/makefile $(filter-out _all sub-make,$(makecmdgoals))</code>

<code>skip-makefile := 1</code>

<code>endif # ifneq ($(kbuild_output),)</code>

<code>endif # ifeq ($(kbuild_src),)</code>

系統會檢查變量 <code>kbuild_src</code>,它代表核心代碼的頂層目錄,如果它是空的(第一次執行 makefile 時總是空的),我們會設定變量 <code>kbuild_output</code> 為傳遞給選項 <code>o</code> 的值(如果這個選項被傳進來了)。下一步會檢查變量 <code>kbuild_output</code> ,如果已經設定好,那麼接下來會做以下幾件事:

将變量 <code>kbuild_output</code> 的值儲存到臨時變量 <code>saved-output</code>;

嘗試建立給定的輸出目錄;

檢查建立的輸出目錄,如果失敗了就列印錯誤;

如果成功建立了輸出目錄,那麼就在新目錄重新執行 <code>make</code> 指令(參見選項<code>-c</code>)。

下一個 <code>ifeq</code> 語句會檢查傳遞給 make 的選項 <code>c</code> 和 <code>m</code>:

<code>ifeq ("$(origin c)", "command line")</code>

<code>kbuild_checksrc = $(c)</code>

<code>ifndef kbuild_checksrc</code>

<code>kbuild_checksrc = 0</code>

<code>ifeq ("$(origin m)", "command line")</code>

<code>kbuild_extmod := $(m)</code>

系統還會檢查變量 <code>kbuild_src</code>,如果 <code>kbuild_src</code> 沒有被設定,系統會設定變量 <code>srctree</code> 為<code>.</code>:

<code>srctree := .</code>

<code>objtree := .</code>

<code>src := $(srctree)</code>

<code>obj := $(objtree)</code>

<code>export srctree objtree vpath</code>

這将會告訴 <code>makefile</code> 核心的源碼樹就在執行 <code>make</code> 指令的目錄,然後要設定 <code>objtree</code> 和其他變量為這個目錄,并且将這些變量導出。接着就是要擷取 <code>subarch</code> 的值,這個變量代表了目前的系統架構(lctt 譯注:一般都指cpu 架構):

<code>subarch := $(shell uname -m | sed -e s/i.86/x86/ -e s/x86_64/x86/ \</code>

<code>-e s/sun4u/sparc64/ \</code>

<code>-e s/arm.*/arm/ -e s/sa110/arm/ \</code>

<code>-e s/s390x/s390/ -e s/parisc64/parisc/ \</code>

<code>-e s/ppc.*/powerpc/ -e s/mips.*/mips/ \</code>

<code>-e s/sh[234].*/sh/ -e s/aarch64.*/arm64/ )</code>

<code>ifeq ($(arch),i386)</code>

<code>srcarch := x86</code>

<code>ifeq ($(arch),x86_64)</code>

<code>hdr-arch := $(srcarch)</code>

注意:<code>arch</code> 是 <code>subarch</code> 的别名。如果沒有設定過代表核心配置檔案路徑的變量 <code>kconfig_config</code>,下一步系統會設定它,預設情況下就是 <code>.config</code> :

<code>kconfig_config ?= .config</code>

<code>export kconfig_config</code>

<code>config_shell := $(shell if [ -x "$$bash" ]; then echo $$bash; \</code>

<code>else if [ -x /bin/bash ]; then echo /bin/bash; \</code>

<code>else echo sh; fi ; fi)</code>

接下來就要設定一組和編譯核心的編譯器相關的變量。我們會設定主機的 <code>c</code> 和 <code>c++</code> 的編譯器及相關配置項:

<code>hostcc = gcc</code>

<code>hostcxx = g++</code>

<code>hostcflags = -wall -wmissing-prototypes -wstrict-prototypes -o2 -fomit-frame-pointer -std=gnu89</code>

<code>hostcxxflags = -o2</code>

接下來會去适配代表編譯器的變量 <code>cc</code>,那為什麼還要 <code>host*</code> 這些變量呢?這是因為 <code>cc</code> 是編譯核心過程中要使用的目标架構的編譯器,但是 <code>hostcc</code> 是要被用來編譯一組 <code>host</code> 程式的(下面我們就會看到)。

然後我們就看到變量 <code>kbuild_modules</code> 和 <code>kbuild_builtin</code> 的定義,這兩個變量決定了我們要編譯什麼東西(核心、子產品或者兩者):

<code>kbuild_modules :=</code>

<code>kbuild_builtin := 1</code>

<code>ifeq ($(makecmdgoals),modules)</code>

<code>kbuild_builtin := $(if $(config_modversions),1)</code>

在這我們可以看到這些變量的定義,并且,如果們僅僅傳遞了 <code>modules</code> 給 <code>make</code>,變量 <code>kbuild_builtin</code> 會依賴于核心配置選項 <code>config_modversions</code>。

下一步操作是引入下面的檔案:

<code>include scripts/kbuild.include</code>

<code>as = $(cross_compile)as</code>

<code>ld = $(cross_compile)ld</code>

<code>cc = $(cross_compile)gcc</code>

<code>cpp = $(cc) -e</code>

<code>ar = $(cross_compile)ar</code>

<code>nm = $(cross_compile)nm</code>

<code>strip = $(cross_compile)strip</code>

<code>objcopy = $(cross_compile)objcopy</code>

<code>objdump = $(cross_compile)objdump</code>

<code>awk = awk</code>

<code>...</code>

在這些定義好的變量後面,我們又定義了兩個變量:<code>userinclude</code> 和 <code>linuxinclude</code>。他們包含了頭檔案的路徑(第一個是給使用者用的,第二個是給核心用的):

<code>userinclude := \</code>

<code>-i$(srctree)/arch/$(hdr-arch)/include/uapi \</code>

<code>-iarch/$(hdr-arch)/include/generated/uapi \</code>

<code>-i$(srctree)/include/uapi \</code>

<code>-iinclude/generated/uapi \</code>

<code>-include $(srctree)/include/linux/kconfig.h</code>

<code>linuxinclude := \</code>

<code>-i$(srctree)/arch/$(hdr-arch)/include \</code>

以及給 c 編譯器的标準标志:

<code>kbuild_cflags := -wall -wundef -wstrict-prototypes -wno-trigraphs \</code>

<code>-fno-strict-aliasing -fno-common \</code>

<code>-werror-implicit-function-declaration \</code>

<code>-wno-format-security \</code>

<code>-std=gnu89</code>

這并不是最終确定的編譯器标志,它們還可以在其他 makefile 裡面更新(比如 <code>arch/</code> 裡面的 kbuild)。變量定義完之後,全部會被導出供其他 makefile 使用。

下面的兩個變量 <code>rcs_find_ignore</code> 和 <code>rcs_tar_ignore</code> 包含了被版本控制系統忽略的檔案:

<code>export rcs_find_ignore := \( -name sccs -o -name bitkeeper -o -name .svn -o \</code>

<code>-name cvs -o -name .pc -o -name .hg -o -name .git \) \</code>

<code>-prune -o</code>

<code>export rcs_tar_ignore := --exclude sccs --exclude bitkeeper --exclude .svn \</code>

<code>--exclude cvs --exclude .pc --exclude .hg --exclude .git</code>

這就是全部了,我們已經完成了所有的準備工作,下一個點就是如果建構<code>vmlinux</code>。

<code>all: vmlinux</code>

<code>include arch/$(srcarch)/makefile</code>

不要操心我們略過的從 <code>export rcs_find_ignore.....</code> 到 <code>all: vmlinux.....</code> 這一部分 makefile 代碼,他們隻是負責根據各種配置檔案(<code>make *.config</code>)生成不同目标核心的,因為之前我就說了這一部分我們隻讨論建構核心的通用途徑。

<code>vmlinux: scripts/link-vmlinux.sh $(vmlinux-deps) force</code>

第二個目标是 <code>vmlinux-deps</code>,它的定義如下:

<code>vmlinux-deps := $(kbuild_lds) $(kbuild_vmlinux_init) $(kbuild_vmlinux_main)</code>

它是由核心代碼下的每個頂級目錄的 <code>built-in.o</code> 組成的。之後我們還會檢查核心所有的目錄,<code>kbuild</code> 會編譯各個目錄下所有的對應 <code>$(obj-y)</code> 的源檔案。接着調用 <code>$(ld) -r</code> 把這些檔案合并到一個 <code>build-in.o</code> 檔案裡。此時我們還沒有<code>vmlinux-deps</code>,是以目标 <code>vmlinux</code> 現在還不會被建構。對我而言 <code>vmlinux-deps</code> 包含下面的檔案:

<code>arch/x86/kernel/vmlinux.lds arch/x86/kernel/head_64.o</code>

<code>arch/x86/kernel/head64.o arch/x86/kernel/head.o</code>

<code>init/built-in.o usr/built-in.o</code>

<code>arch/x86/built-in.o kernel/built-in.o</code>

<code>mm/built-in.o fs/built-in.o</code>

<code>ipc/built-in.o security/built-in.o</code>

<code>crypto/built-in.o block/built-in.o</code>

<code>lib/lib.a arch/x86/lib/lib.a</code>

<code>lib/built-in.o arch/x86/lib/built-in.o</code>

<code>drivers/built-in.o sound/built-in.o</code>

<code>firmware/built-in.o arch/x86/pci/built-in.o</code>

<code>arch/x86/power/built-in.o arch/x86/video/built-in.o</code>

<code>net/built-in.o</code>

下一個可以被執行的目标如下:

<code>$(sort $(vmlinux-deps)): $(vmlinux-dirs) ;</code>

<code>$(vmlinux-dirs): prepare scripts</code>

<code>$(q)$(make) $(build)=$@</code>

就像我們看到的,<code>vmlinux-dir</code> 依賴于兩部分:<code>prepare</code> 和 <code>scripts</code>。第一個 <code>prepare</code> 定義在核心的根<code>makefile</code> 中,準備工作分成三個階段:

<code>prepare: prepare0</code>

<code>prepare0: archprepare force</code>

<code>$(q)$(make) $(build)=.</code>

<code>archprepare: archheaders archscripts prepare1 scripts_basic</code>

<code>prepare1: prepare2 $(version_h) include/generated/utsrelease.h \</code>

<code>include/config/auto.conf</code>

<code>$(cmd_crmodverdir)</code>

<code>prepare2: prepare3 outputmakefile asm-generic</code>

第一個目标是 makefile 生成的系統調用清單(syscall table)中的 <code>archheaders</code> :

<code>archheaders:</code>

<code>$(q)$(make) $(build)=arch/x86/entry/syscalls all</code>

第二個目标是 makefile 裡的 <code>archscripts</code>:

<code>archscripts: scripts_basic</code>

<code>$(q)$(make) $(build)=arch/x86/tools relocs</code>

<code>scripts_basic:</code>

<code>$(q)$(make) $(build)=scripts/basic</code>

<code>scripts/basic/makefile</code> 包含了編譯兩個主機程式 <code>fixdep</code> 和 <code>bin2</code> 的目标:

<code>hostprogs-y := fixdep</code>

<code>hostprogs-$(config_build_bin2c) += bin2c</code>

<code>always := $(hostprogs-y)</code>

<code>$(addprefix $(obj)/,$(filter-out fixdep,$(always))): $(obj)/fixdep</code>

執行 make 之後,終端的第一個輸出就是 <code>kbuild</code> 的結果:

<code>$ make</code>

<code>hostcc scripts/basic/fixdep</code>

<code>hostcc arch/x86/tools/relocs_32.o</code>

<code>hostcc arch/x86/tools/relocs_64.o</code>

<code>hostcc arch/x86/tools/relocs_common.o</code>

<code>hostld arch/x86/tools/relocs</code>

在編譯完 <code>relocs.c</code> 之後會檢查 <code>version.h</code>:

<code>$(version_h): $(srctree)/makefile force</code>

<code>$(call filechk,version.h)</code>

<code>$(q)rm -f $(old_version_h)</code>

我們可以在輸出看到它:

<code>chk include/config/kernel.release</code>

以及在核心的根 makefiel 使用 <code>arch/x86/include/generated/asm</code> 的目标 <code>asm-generic</code> 來建構 <code>generic</code>彙編頭檔案。在目标 <code>asm-generic</code> 之後,<code>archprepare</code> 就完成了,是以目标 <code>prepare0</code> 會接着被執行,如我上面所寫:

<code>build := -f $(srctree)/scripts/makefile.build obj</code>

或者在我們的例子中,它就是目前源碼目錄路徑:<code>.</code>:

<code>$(q)$(make) -f $(srctree)/scripts/makefile.build obj=.</code>

<code>include $(kbuild-file)</code>

首先,我們先來了解一下 <code>vmlinux-dirs</code> 都包含了那些東西。在我們的例子中它包含了下列核心目錄的路徑:

<code>init usr arch/x86 kernel mm fs ipc security crypto block</code>

<code>drivers sound firmware arch/x86/pci arch/x86/power</code>

<code>arch/x86/video net lib arch/x86/lib</code>

<code>vmlinux-dirs := $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) \</code>

<code>$(core-y) $(core-m) $(drivers-y) $(drivers-m) \</code>

<code>$(net-y) $(net-m) $(libs-y) $(libs-m)))</code>

<code>init-y := init/</code>

<code>drivers-y := drivers/ sound/ firmware/</code>

<code>net-y := net/</code>

<code>libs-y := lib/</code>

這裡我們借助函數 <code>patsubst</code> 和 <code>filter</code>去掉了每個目錄路徑裡的符号 <code>/</code>,并且把結果放到 <code>vmlinux-dirs</code>裡。是以我們就有了 <code>vmlinux-dirs</code> 裡的目錄清單,以及下面的代碼:

符号 <code>$@</code> 在這裡代表了 <code>vmlinux-dirs</code>,這就表明程式會遞歸周遊從 <code>vmlinux-dirs</code> 以及它内部的全部目錄(依賴于配置),并且在對應的目錄下執行 <code>make</code> 指令。我們可以在輸出看到結果:

<code>cc init/main.o</code>

<code>chk include/generated/compile.h</code>

<code>cc init/version.o</code>

<code>cc init/do_mounts.o</code>

<code>cc arch/x86/crypto/glue_helper.o</code>

<code>as arch/x86/crypto/aes-x86_64-asm_64.o</code>

<code>cc arch/x86/crypto/aes_glue.o</code>

<code>as arch/x86/entry/entry_64.o</code>

<code>as arch/x86/entry/thunk_64.o</code>

<code>cc arch/x86/entry/syscall_64.o</code>

每個目錄下的源代碼将會被編譯并且連結到 <code>built-io.o</code> 裡:

<code>$ find . -name built-in.o</code>

<code>./arch/x86/crypto/built-in.o</code>

<code>./arch/x86/crypto/sha-mb/built-in.o</code>

<code>./arch/x86/net/built-in.o</code>

<code>./init/built-in.o</code>

<code>./usr/built-in.o</code>

<code>+$(call if_changed,link-vmlinux)</code>

<code>link vmlinux</code>

<code>ld vmlinux.o</code>

<code>modpost vmlinux.o</code>

<code>gen .version</code>

<code>upd include/generated/compile.h</code>

<code>ld init/built-in.o</code>

<code>ksym .tmp_kallsyms1.o</code>

<code>ksym .tmp_kallsyms2.o</code>

<code>ld vmlinux</code>

<code>sortex vmlinux</code>

<code>sysmap system.map</code>

<code>vmlinux</code> 和<code>system.map</code> 生成在核心源碼樹根目錄下。

<code>$ ls vmlinux system.map</code>

<code>system.map vmlinux</code>

<code>all: bzimage</code>

<code>bzimage: vmlinux</code>

<code>$(q)$(make) $(build)=$(boot) $(kbuild_image)</code>

<code>$(q)mkdir -p $(objtree)/arch/$(uts_machine)/boot</code>

<code>$(q)ln -fsn ../../x86/boot/bzimage $(objtree)/arch/$(uts_machine)/boot/$@</code>

在這裡我們可以看到第一次為 boot 目錄執行 <code>make</code>,在我們的例子裡是這樣的:

<code>boot := arch/x86/boot</code>

<code>$(obj)/setup.elf: $(src)/setup.ld $(setup_objs) force</code>

<code>$(call if_changed,ld)</code>

我們已經在目錄 <code>arch/x86/boot</code> 有了連結腳本 <code>setup.ld</code>,和擴充到 <code>boot</code> 目錄下全部源代碼的變量<code>setup_objs</code> 。我們可以看看第一個輸出:

<code>as arch/x86/boot/bioscall.o</code>

<code>cc arch/x86/boot/cmdline.o</code>

<code>as arch/x86/boot/copy.o</code>

<code>hostcc arch/x86/boot/mkcpustr</code>

<code>cpustr arch/x86/boot/cpustr.h</code>

<code>cc arch/x86/boot/cpu.o</code>

<code>cc arch/x86/boot/cpuflags.o</code>

<code>cc arch/x86/boot/cpucheck.o</code>

<code>cc arch/x86/boot/early_serial_console.o</code>

<code>cc arch/x86/boot/edd.o</code>

<code>$(obj)/header.o: $(obj)/voffset.h $(obj)/zoffset.h</code>

第一個頭檔案 <code>voffset.h</code> 是使用 <code>sed</code> 腳本生成的,包含用 <code>nm</code> 工具從 <code>vmlinux</code> 擷取的兩個位址:

<code>#define vo__end 0xffffffff82ab0000</code>

<code>#define vo__text 0xffffffff81000000</code>

<code>$(obj)/zoffset.h: $(obj)/compressed/vmlinux force</code>

<code>$(call if_changed,zoffset)</code>

<code>lds arch/x86/boot/compressed/vmlinux.lds</code>

<code>as arch/x86/boot/compressed/head_64.o</code>

<code>cc arch/x86/boot/compressed/misc.o</code>

<code>cc arch/x86/boot/compressed/string.o</code>

<code>cc arch/x86/boot/compressed/cmdline.o</code>

<code>objcopy arch/x86/boot/compressed/vmlinux.bin</code>

<code>bzip2 arch/x86/boot/compressed/vmlinux.bin.bz2</code>

<code>hostcc arch/x86/boot/compressed/mkpiggy</code>

<code>vmlinux.bin</code> 是去掉了調試資訊和注釋的 <code>vmlinux</code> 二進制檔案,加上了占用了 <code>u32</code> (lctt 譯注:即4-byte)的長度資訊的 <code>vmlinux.bin.all</code> 壓縮後就是 <code>vmlinux.bin.bz2</code>。其中 <code>vmlinux.bin.all</code> 包含了<code>vmlinux.bin</code> 和<code>vmlinux.relocs</code>(lctt 譯注:vmlinux 的重定位資訊),其中 <code>vmlinux.relocs</code> 是<code>vmlinux</code> 經過程式 <code>relocs</code> 處理之後的 <code>vmlinux</code> 鏡像(見上文所述)。我們現在已經擷取到了這些檔案,彙編檔案 <code>piggy.s</code> 将會被 <code>mkpiggy</code> 生成、然後編譯:

<code>mkpiggy arch/x86/boot/compressed/piggy.s</code>

<code>as arch/x86/boot/compressed/piggy.o</code>

這個彙編檔案會包含經過計算得來的、壓縮核心的偏移資訊。處理完這個彙編檔案,我們就可以看到 <code>zoffset</code>生成了:

<code>zoffset arch/x86/boot/zoffset.h</code>

<code>as arch/x86/boot/header.o</code>

<code>cc arch/x86/boot/main.o</code>

<code>cc arch/x86/boot/mca.o</code>

<code>cc arch/x86/boot/memory.o</code>

<code>cc arch/x86/boot/pm.o</code>

<code>as arch/x86/boot/pmjump.o</code>

<code>cc arch/x86/boot/printf.o</code>

<code>cc arch/x86/boot/regs.o</code>

<code>cc arch/x86/boot/string.o</code>

<code>cc arch/x86/boot/tty.o</code>

<code>cc arch/x86/boot/video.o</code>

<code>cc arch/x86/boot/video-mode.o</code>

<code>cc arch/x86/boot/video-vga.o</code>

<code>cc arch/x86/boot/video-vesa.o</code>

<code>cc arch/x86/boot/video-bios.o</code>

所有的源代碼會被編譯,他們最終會被連結到 <code>setup.elf</code> :

<code>ld arch/x86/boot/setup.elf</code>

或者:

<code>ld -m elf_x86_64 -t arch/x86/boot/setup.ld arch/x86/boot/a20.o arch/x86/boot/bioscall.o arch/x86/boot/cmdline.o arch/x86/boot/copy.o arch/x86/boot/cpu.o arch/x86/boot/cpuflags.o arch/x86/boot/cpucheck.o arch/x86/boot/early_serial_console.o arch/x86/boot/edd.o arch/x86/boot/header.o arch/x86/boot/main.o arch/x86/boot/mca.o arch/x86/boot/memory.o arch/x86/boot/pm.o arch/x86/boot/pmjump.o arch/x86/boot/printf.o arch/x86/boot/regs.o arch/x86/boot/string.o arch/x86/boot/tty.o arch/x86/boot/video.o arch/x86/boot/video-mode.o arch/x86/boot/version.o arch/x86/boot/video-vga.o arch/x86/boot/video-vesa.o arch/x86/boot/video-bios.o -o arch/x86/boot/setup.elf</code>

最後的兩件事是建立包含目錄 <code>arch/x86/boot/*</code> 下的編譯過的代碼的 <code>setup.bin</code>:

<code>objcopy -o binary arch/x86/boot/setup.elf arch/x86/boot/setup.bin</code>

以及從 <code>vmlinux</code> 生成 <code>vmlinux.bin</code> :

<code>objcopy -o binary -r .note -r .comment -s arch/x86/boot/compressed/vmlinux arch/x86/boot/vmlinux.bin</code>

<code>arch/x86/boot/tools/build arch/x86/boot/setup.bin arch/x86/boot/vmlinux.bin arch/x86/boot/zoffset.h arch/x86/boot/bzimage</code>

實際上 <code>bzimage</code> 就是把 <code>setup.bin</code> 和 <code>vmlinux.bin</code> 連接配接到一起。最終我們會看到輸出結果,就和那些用源碼編譯過核心的同行的結果一樣:

<code>setup is 16268 bytes (padded to 16384 bytes).</code>

<code>system is 4704 kb</code>

<code>crc 94a88f9a</code>

<code>kernel: arch/x86/boot/bzimage is ready (#5)</code>

全部結束。

這就是本文的結尾部分。本文我們了解了編譯核心的全部步驟:從執行 <code>make</code> 指令開始,到最後生成<code>bzimage</code>。我知道,linux 核心的 makefile 和建構 linux 的過程第一眼看起來可能比較迷惑,但是這并不是很難。希望本文可以幫助你了解建構 linux 核心的整個流程

本文來自雲栖社群合作夥伴“linux中國”,原文釋出日期:2015-09-11

繼續閱讀