内核是启动时加载到内存中的第一个组件。然后,在整个计算机使用过程中,它一直保留在内存中。
内核是Linux系统的核心。它提供了一个用户界面,使我们能够与操作系统进行交互。这将应用程序与底层硬件连接起来:处理进程管理、内存管理、设备驱动程序和系统安全等任务。
此外,它还管理系统资源,提供基本服务,并实现软件和硬件之间的通信。它控制系统的内存、进程和硬件。
在某些情况下,我们可能需要生成和构造自定义内核映像。这可能是由于以下原因造成的:
当我们编译自定义内核时,我们可以将其生成为以下内核映像之一:vmlinux,vmlinuz,vmlinux.bin,zimage和bzimage。 内核映像是包含内核代码和其他组件的二进制表示形式的文件。
内核映像格式因压缩、体系结构和特定用例而异。让我们看看我们拥有的各种 Linux 内核映像。
原始的、未压缩的 Linux 内核映像称为 vmlinux。vmlinux 是未压缩且不可引导形式的内核。这是生产vmlinuz的中间步骤。它包含调试符号以及完整且未修改的内核代码。
在大多数情况下,vmlinux 用于开发内核、调试和分析它们。vmlinux 映像在加载到操作系统内核之前应该是可引导的。为了使它可引导,我们添加了一个多引导标头、引导扇区并设置例程。
Linux 前面的“vm”代表虚拟内存。在Linux中,我们可以使用一部分硬盘空间作为虚拟内存,因此得名“vm”。
vmlinuz是一个压缩的Linux内核映像文件,用于引导Linux操作系统。当我们压缩vmlinux文件时,我们创建了vmlinuz。压缩使用 gzip 算法,导致文件大小小于未压缩的 vmlinux。
生成的文件包含内核的基本组件。压缩可减小文件大小,优化启动效率和启动期间的内存使用。
当我们引导系统时,引导加载程序会从引导设备读取 vmlinuz 文件并将其解压缩到内存中。解压缩的内核映像从内存运行。
vmlinux.bin 文件是在 Linux 内核源代码编译过程中生成的未压缩的二进制映像。它包括整个编译的内核代码,以及调试符号和用于调试和分析的附加信息。
vmlinux.bin 映像文件不能直接执行,并且太大而无法实际使用。因此,开发人员使用它来理解和分析 Linux 内核行为。
VMLinux.bin 提供了更高的灵活性,允许根据特定要求进行定制。
zimage 是指一种独特的压缩内核映像文件格式。zImage是vmlinux经过gzip压缩后的文件。它解决了无法处理大型压缩内核映像的旧引导加载程序的限制。
我们使用称为LZ77的压缩算法压缩zimage。LZ77压缩算法优化了速度,完美平衡了压缩比和解压缩性能。LZ77 压缩创建的图像文件比 bzimage 小。
我们必须注意,zimage 格式是典型的 x86 架构,其他架构可能不支持它。
bzimage 是指 Linux 引导加载程序用于在系统引导过程中加载和初始化内核的压缩内核映像文件。引导加载程序从引导设备读取 bzimage 文件并将其解压缩到内存中。然后,它将控制权转移到解压缩的内核映像,该映像继续引导过程。
bzimage 文件是编译 Linux 内核源代码的副产品,其中包括内核的核心功能、设备驱动程序和其他重要元素。当我们编译内核时,它会生成 vmlinux。但是,vmlinux 太大,无法放入引导期间可用的有限内存(RAM 的前 640KB)。 因此,我们使用 gzip 实用程序将 vmlinux 文件压缩为较小的大小(通常压缩到 512KB 以下),从而创建 bzimage 映像文件。
压缩过程显著减小了内核映像的大小,使引导过程更容易。bzimage 中的“bz”代表“大压缩”,因为我们使用 gzip 压缩算法压缩内核映像。
让我们看一下 bzimage 文件中存在的一些组件。首先,我们有引导加载程序标头,其中包含引导加载程序加载和执行内核所需的信息。此标头提供详细信息,例如内核版本、压缩内核的大小、压缩内核的偏移量以及初始 RAM 磁盘 (initrd) 的位置(如果存在)。
initrd 是在挂载实际根文件系统之前的引导过程中加载到内存中的临时根文件系统。
然后,我们有包含压缩内核映像的 bzimage 文件。此映像包括初始化内核和启动操作系统所需的所有代码和数据。它由内核的入口点、初始化例程、设备驱动程序、文件系统等组成。
一般编译器链接生成的文件都是一个ELF格式的可执行文件,对于内核来说也就是经过LD后生成vmlinux,然后利用OBJCOPY工具处理这个EFL文件,去除其中的符号和重定位信息等等,生成一个完全的二进制文件Image。
uImage是U-boot专用的映像文件,它是在zImage之前加上一个长度为0x40的“头”,说明这个映像文件的类型、加载位置、生成时间、大小等信息。换句话说,如果直接从uImage的0x40位置开始执行,zImage和uImage没有任何区别。
在本文中,我们看到了内核映像格式,它们为我们提供了部署和引导 Linux 内核的不同选项。vmlinux 是未压缩的内核代码,Image是去除其中的符号和重定位信息等等,生成一个完全的二进制文件。vmlinuz 和 vmlinux.bin 是用于引导的压缩版本。zimage 是一种较旧的压缩格式,bzImage 是改进版本,uImage 则是与uboot结合的版本。
最后,选择正确的内核映像格式取决于多种因素,例如用例、硬件架构、引导加载程序兼容性和压缩/优化要求。
以编译一个压缩后的内核镜像为例:
make zImage
最后链接过程的log如下所示:
LD vmlinux
SYSMAP System.map
SYSMAP .tmp_System.map
OBJCOPY arch/arm/boot/Image
Kernel: arch/arm/boot/Image is ready
AS arch/arm/boot/compressed/head.o
GZIP arch/arm/boot/compressed/piggy.gz
AS arch/arm/boot/compressed/piggy.o
CC arch/arm/boot/compressed/misc.o
AS arch/arm/boot/compressed/head-xscale.o
LD arch/arm/boot/compressed/vmlinux
OBJCOPY arch/arm/boot/zImage
可以看到这个命令会先生成vmlinux,然后经过OBJCOPY操作生成对应的镜像。内核编译都会先生成未经过压缩的镜像,然后在生成压缩的镜像。以上过程可以分为两个部分:
第一步生成未经过压缩的vmlinux、Image
第二步生成压缩的vmlinux、zImage
对应关系为:
未压缩的内核镜像生成过程:
vmlinux ---OBJCOPY---> arch/arm/boot/Image
压缩的内核镜像生成过程:
arch/arm/boot/compressed/vmlinux ---OBJCOPY ---> arch/arm/boot/zImage
一般编译器链接生成的文件都是一个ELF格式的可执行文件,对于内核来说也就是经过LD后生成vmlinux,然后利用OBJCOPY工具处理这个EFL文件,去除其中的符号和重定位信息等等,生成一个完全的二进制文件Image。因此当我们需要debug内核时,一定是需要有内核版本对应的vmlinux文件,因为它其中包含了符号信息。可以利用 readelf -s vmlinux 来查看其中的内核符号。