談談Linux內核鏡像


樹莓派官方提供的Raspberry Pi OS固件中,boot分區內核的鏡像文件命名為kernel7.img,而ubuntu官方提供的固件中,boot分區內核的鏡像文件則為vmlinuz,這兩者有什么區別?如何生成對應的鏡像文件?

這里,我們有必要了解,常見的嵌入式系統中,內核鏡像的格式有哪些,如何生成和使用。

 

1、內核鏡像格式

1.1 vmlinux

vmlinux是整個內核編譯的直接產物。這里提及的是“編譯”。顯然,內核主要是C語言實現,編譯器對內核編譯,同樣是一般的編譯過程:預處理、編譯、匯編和鏈接。從文件的角度,它是ELF格式文件。比如我們通過gcc對*.c進行編譯得到的可執行文件,就是屬於ELF格式文件。這里vm代表Virtual Memory,Linux支持虛擬內存,因此得名vm。該文件一般在生成在內核源碼根目錄下。

通過readelf工具查看vmlinux的文件頭信息:

readelf -h vmlinux:

ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              DYN (Shared object file)
  Machine:                           AArch64
  Version:                           0x1
  Entry point address:               0xffffffc010080000
  Start of program headers:          64 (bytes into file)
  Start of section headers:          15358864 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         5
  Size of section headers:           64 (bytes)
  Number of section headers:         32
  Section header string table index: 29

以上可以看到,該內核鏡像文件是在aarch64上運行的。

 

1.2 Image

在編譯完內核時候,有如下打印信息:

OBJCOPY arch/arm64/boot/Image

的確,Image就在上面的目錄下。那么OBJCOPY是什么操作?我們來看看實際執行的命令:

./arch/arm64/boot/.Image.cmd:1:cmd_arch/arm64/boot/Image := aarch64-linux-gnu-objcopy  -O binary -R .note -R .note.gnu.build-id -R .comment -S vmlinux arch/arm64/boot/Image

該命令的解析可以參照如下:

objcopy -O binary -R .note -R .comment -S *.elf  *.bin
#使用 -O binary (或--out-target=binary) 輸出為原始的二進制文件
#使用 -R .note  (或--remove-section)    輸出文件中不要.note這個section,縮小了文件尺寸
#使用 -R .comment(或--remove-section)   輸出文件中不要.comment這個section,縮小了文件尺寸
#使用 -S (或 --strip-all)輸出文件中不要重定位信息和符號信息,縮小了文件尺寸 

也即:Image是在vmlinux基礎上,去掉不必要信息的二進制碼(binary)

 

1.3 Image.gz

在編譯樹莓派64內核時,在boot目錄下,也會發現該鏡像文件。在編譯過程,有:

GZIP arch/arm64/boot/Image.gz

實際上,Image.gz就是通過GZIP壓縮工具對上述的Image進行壓縮的產物。在編譯時,也可以指定make image.gz。注意到,樹莓派的Ubuntu提供的固件,其boot區存放的是vmlinuz,其實就是該壓縮的Image,只不過是重命名為vmlinuz罷了。因此,也可以手動來生產樹莓派ubuntu-arm64對應的內核鏡像:

gzip Image   #執行后,Image將會變成Image.gz
cp Image.gz vmlinuz

arm64是在linux 3.7版本及之后方導入的arch,也是至此之后,內核生成aarch64的內核鏡像都是通過gzip直接對Image壓縮的,這和arm(32)的內核鏡像不一樣(詳見后述)。如下是官方的說明:

The AArch64 kernel does not currently provide a decompressor and
therefore requires decompression (gzip etc.) to be performed by the boot
loader if a compressed Image target (e.g. Image.gz) is used.  For
bootloaders that do not implement this requirement, the uncompressed
Image target is available instead.

也即:內核鏡像vmlinuz/Image.gz,不再(像zImage)那樣帶有自解壓代碼,而是需要BootLoader去完成該部分工作,否則,就只能直接使用非解壓的Image鏡像。

 

1.4 zImage

我們在編譯樹莓派32位內核時,會有如下打印信息,基本可以宏觀上看到整個生成過程。

 

LD      vmlinux.o
  MODPOST vmlinux.o
  KSYM    .tmp_kallsyms1.o
  KSYM    .tmp_kallsyms2.o
  LD      vmlinux
  SORTEX  vmlinux
  SYSMAP  System.map
  OBJCOPY arch/arm/boot/Image
  Kernel: arch/arm/boot/Image is ready
  LDS     arch/arm/boot/compressed/vmlinux.lds
  AS      arch/arm/boot/compressed/head.o
  GZIP    arch/arm/boot/compressed/piggy_data
  CC      arch/arm/boot/compressed/misc.o
  CC      arch/arm/boot/compressed/decompress.o
  CC      arch/arm/boot/compressed/string.o
  AS      arch/arm/boot/compressed/hyp-stub.o
  AS      arch/arm/boot/compressed/lib1funcs.o
  AS      arch/arm/boot/compressed/ashldi3.o
  AS      arch/arm/boot/compressed/bswapsdi2.o
  AS      arch/arm/boot/compressed/piggy.o
  LD      arch/arm/boot/compressed/vmlinux
  OBJCOPY arch/arm/boot/zImage
  Kernel: arch/arm/boot/zImage is ready

首先,zImage是基於Image生成的,和Image.gz不一樣的地方是,zImage包含了自解壓部分代碼,並且不是直接壓縮的產物。上面log也看到,piggy_data是在Image基礎上壓縮的,類似於Image.gz,同時與head.o/misc.o等,鏈接成vmlinux(顯然該compress目錄下的vmlinux不同於boot目錄下的vmlinux)。最后,通過OBJCOPY,同樣去掉一些不必要信息(符號、注釋、調試信息等),得到最終的zImage。該鏡像可以通過make zImage來生成。

因此,zImage是帶有自解壓代碼的內核鏡像文件。

 

 1.5 bzImage

bzImage是通過make bzImage命令來生成,其生成過程與zImage一樣。如果內核比較小,那么可以采用zImage或bzImage之一,兩種方式引導的系統運行 時是相同的。大的內核采用bzImage,不能采用zImage。

 

1.6 uImage

uImage,從名字看,可以簡單理解為,給uboot使用的內核鏡像。具體地,它是使用uboot工具mkimage對普通的壓縮內核映像文件(zImage)加工而得。具體怎樣加工?很簡單,就是在zImage之前添加64個字節的頭,說明這個內核的版本、加載位置、生成時間、大小等信息,這些信息供uboot加載內核的時候使用。

 

2、總結

內核編譯,首先得到的是vmlinux的ELF文件,可以通過OBJCOPY命令來去掉不必要的信息(符號、注釋、調試等),得到Image。在64位內核中,通過GZIP對Image直接壓縮,得到Image.gz,在一些系統中,還重命名為vmlinuz。由於不帶解壓程序,因此內核的加載之前,還需要BootLoader去做解壓。對32位內核,通常使用帶自解壓程序的zImage,該鏡像是Image基礎上進行壓縮,然后與head.o/misc.o鏈接,再經過OBJCOPY的獲取到的鏡像,因此不是簡單的GZIP壓縮(也不能通過GZIP來解壓使用)。

根據前面的分析,我們將各類鏡像的生成過程通過如下圖直觀表示出來:

 

 

 

 

 

 

回到本文開始提到的問題,您是否得到答案了呢?

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM