柴米油鹽計劃

Kbuild 編譯 Linux 內核系列(八)

Author: Wei Yang
作者公眾號:楊小偉的世界

原來安裝的是它

編譯完內核,接下來要做的就是安裝新的內核來使用了。那安裝的是哪個文件呢?

關於這個問題,昨天我還真好好看了一下,發現原來安裝的不是根目錄下的 vmlinux ,而是 bzImage 。玩了內核這麼多年,一直都以為是根目錄下的那個 vmlinux 是安裝時拷貝到 /boot 目錄下的文件,結果原來不是。真是慚愧慚愧。

我是從 make install 這個規則開始找下去的。

boot := arch/x86/boot
install:
    $(Q)$(MAKE) $(build)=$(boot) $@

這個規則在 arch/x86/Makefile 中,好奇為什麼沒有在根 Makefile 里找到。

那實際上真正執行的是在 arch/x86/boot/Makefile 中這個規則

install:
    sh $(srctree)/$(src)/install.sh $(KERNELRELEASE) $(obj)/bzImage 
        System.map "$(INSTALL_PATH)"

對應 x86 架構,在這個 install.sh 就是 arch/x86/boot/install.sh 。雖然腳本中有幾種安裝內核的方式,不過我們只看其中一種也就能確認安裝在 /boot 目錄下的是 bzImage 而不是 vmlinux 了。

cat $2 > $4/vmlinuz

所以說不看不知道,一看嚇一跳。以後不敢說自己懂內核了。

目標在哪裡?

我們在內核編譯的小目標一文中也提到過, bzImage 是 x86 平台下默認的目標之一。但是並沒有在根目錄的 Makefile 中發現 bzImage 目標。 而在根目錄的 Makefile 中的前面部分有

include $(srctree)/arch/$(SRCARCH)/Makefile

這個是不同的 arch 會 include 不同的文件,比如是 x86 的架構就會 include arch/x86/Makefile 。
打開一看,果不其然。

# Default kernel to build
all: bzImage

好了,我們終於找到這個 bzImage 的 target 了,在 x86 平台它也是 all 的一部分。
來看看具體是什麼。

生成規則

在 arch/x86/Makefile 中, bzImage 具體定義是這麼個樣子的。

# KBUILD_IMAGE specify target image being built
KBUILD_IMAGE := $(boot)/bzImage

bzImage: vmlinux
ifeq ($(CONFIG_X86_DECODER_SELFTEST),y)
    $(Q)$(MAKE) $(build)=arch/x86/tools posttest
endif
    $(Q)$(MAKE) $(build)=$(boot) $(KBUILD_IMAGE)
    $(Q)mkdir -p $(objtree)/arch/$(UTS_MACHINE)/boot
    $(Q)ln -fsn ../../x86/boot/bzImage $(objtree)/arch/$(UTS_MACHINE)/boot/$@

又是一坨這麼長的,整的好生心煩。幸好,看到了一個眼熟的 $(MAKE) $(build)=$(boot) $(KBUILD_IMAGE) 。這個不是我們編譯具體目標的時候經常看到的么?咱來展開看一眼:

make -f scripts/Makefile.build obj=arch/x86/boot arch/x86/boot/bzImage

是不是覺得好像親切了一些?
對 scripts/Makefile.build 文件再補充一點,在該文件的開頭處出了包含了 scripts/Kbuild.include 文件,還包含了一個 $(build-file) 文件。
先來看一下這個變數的定義:

#The filename Kbuild has precedence over Makefile
kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)
include $(kbuild-file)

你猜到了什麼不? 沒猜到? 再看一眼上面的注釋?
對了,這個就是單個目錄下符合 kbuild 系統的規則文件。
你還記得編譯一個內核模塊時候那個 Makefile 中定義的 obj-y , obj-m 么?為什麼我們在內核模塊中的規則文件只需要定義這幾個變數,就可以編譯出目標文件和模塊呢?原因就是在 scripts/Makefile.build 中包含了目標目錄下的規則文件。

不懂也沒關係,看多了日後自然知曉。
先看一下當前包含的文件,這次包含的是 arch/x86/boot/Makefile 。裡面有這麼一句。

quiet_cmd_image = BUILD   $@
cmd_image = $(obj)/tools/build $(obj)/setup.bin $(obj)/vmlinux.bin 
                   $(obj)/zoffset.h $@

$(obj)/bzImage: $(obj)/setup.bin $(obj)/vmlinux.bin $(obj)/tools/build FORCE
    $(call if_changed,image)
    @echo 'Kernel: $@ is ready' ' (#'`cat .version`')'

簡單明了,會心一笑。
所以最後的最後是通過 tools/build 這個用戶態工具生成 bzImage 文件,而依賴的文件是 setup.bin 和 vmlinux.bin 。
當然加上絕對路徑後,這幾個文件分別是。

arch/x86/boot/tools/build
arch/x86/boot/setup.bin
arch/x86/boot/vmlinux.bin
arch/x86/boot/zoffset.h arch/x86/boot/bzImage                                                                              

看看這個 build 都做了什麼~

這是一個用戶態的程序,文件在 arch/x86/boot/tools/build.c 。雖然說 main 函數的主體結構相對簡潔清晰,但實際上包含了不少文件格式相關的檢查和動態填充。不少細節我也不是很懂,而且本主題關注的還是 bzImage 編譯過程,所以細節部分就暫且略過。

來看我們能看得明白的,按照相關線索抽取了代碼。
回憶一下最後 build 的命令。

$(obj)/tools/build $(obj)/setup.bin $(obj)/vmlinux.bin 
                   $(obj)/zoffset.h $@

拷貝 setup.bin

    dest = fopen(argv[4], "w");

    file = fopen(argv[1], "r");
    c = fread(buf, 1, sizeof(buf), file);

    if (fwrite(buf, 1, i, dest) != i)
        die("Writing setup failed");
  • 先打開了 bzImage ,叫 dest 。
  • 然後讀取 setup.bin 到 buf 。
  • 最後寫入 bzImage 。

拷貝 vmlinux.bin

    fd = open(argv[2], O_RDONLY);

    kernel = mmap(NULL, sz, PROT_READ, MAP_SHARED, fd, 0);

    /* Copy the kernel code */
    crc = partial_crc32(kernel, sz, crc);
    if (fwrite(kernel, 1, sz, dest) != sz)
        die("Writing kernel failed");
  • 先打開了 vmlinux.bin 。
  • 進行 map 文件映射。
  • 拷貝到了 dest 指向的 bzImage 。

是不是也挺簡單的呢。

一張圖顯示依賴關係

         setup.bin    vmlinux.bin  
                        /
                       /
                 bzImage

丑是丑了點,畢竟清晰了些。

心中的疑惑

bzImage 的編譯過程就告一段落,然而心中的疑惑更多了。作為保存在磁碟上的一個文件, grub 又是如何載入?載入到內存的哪個位置?載入時首先執行的是哪條指令?bzImage 的兩個組成部分 vmlinux.bin 和 setup.bin 又是怎麼出現的呢?

路漫漫其修遠兮,吾將上下而求索。


本文是 LinuxStory 柴米油鹽計劃的投稿文章,由 LinuxStory 整理髮布。
原作者署名為: Wei Yang 。
轉載請註明出處,否則必究相關責任。
本文鏈接:https://linuxstory.org/kbuild-linux-8/

對這篇文章感覺如何?

太棒了
0
不錯
0
愛死了
0
不太好
0
感覺很糟
0
這裡是柴米油鹽計劃投稿的發布賬號。

    You may also like

    Leave a reply

    您的郵箱地址不會被公開。 必填項已用 * 標註

    此站點使用Akismet來減少垃圾評論。了解我們如何處理您的評論數據

    柴米油鹽計劃

    VIM 使用演示

    此視頻來自 LinuxStory 志願者的投稿,他堅信: 當一件事做到足夠多時,便會有質的改變。 你說呢?來看看他又學習了什麼新技能吧!
    柴米油鹽計劃

    C 語言總結

    本文來自 wybuhui 的投稿截圖,原稿是 PDF 格式,如果不想看圖片,可以到文末地址下載原文 PDF 文件。 下面讓我們一起欣賞這篇佳作吧。 原文鏈接:PDF 文件地址 本文鏈接:https:/ […]