Linux 之九 详解 Kernel 构建流程、Image 介绍、Image 使用说明

  在之前的博文 Linux 之八 完整嵌入式 Linux 环境、(交叉)编译工具链、CPU 体系架构、嵌入式系统构建工具 中说了要一步步搭建整个嵌入式 Linux 运行环境,今天就开始编译 Linux Kernel。

源码

  文中涉及的源代码均放到了我个人的 Github 上:https://github.com/ZCShou/BOARD-STM32F769I-EVAL这个仓库中包含了要搭建的完整嵌入式 Linux 环境的所有源代码,后续博文均以该仓库中的源码为基础来学习!
在这里插入图片描述

开发环境

  我这里使用的基本开发环境依旧是在之前博文中多次说明的 Ubuntu 22.04.1 LTS + Arm GNU Toolchain 11.3.Rel1,对应的 J-link 也依旧是 J-Link_Linux_V764e_x86_64.deb。该环境下需要注意的问题说明如下:
在这里插入图片描述

  1. 由于 Ubuntu 22.04 LTS 默认是标配 OpenSSL 3.x,而旧版 U-Boot 使用的是 OpenSSL 1.x,所以,该环境编译旧版 U-Boot(从 commit e927e21c 开始添加了相关处理) 将出现一堆警告,因此,后续使用 u-boot-v2022.10 这个版本为主:
    在这里插入图片描述
  2. Arm GNU Toolchain 10.2-2022.02 存在 BUG,导致编译 U-Boot 报错,不要使用这个版本!
    在这里插入图片描述
  3. 新版(Arm GNU Toolchain 10.3 之后的版本)的 Arm GNU Toolchain 在 Linux 上 GDB 需要 Python3.8 支持。然而,Ubuntu 22.04 默认的 Python 是 3.10。直接运行 arm-none-eabi-gdb 报错如下:
    在这里插入图片描述
    解决方法就是直接手动安装 Python3.8 即可。 旧版的 Arm GNU Toolchain 10.3 -2021.10 不需要 Python 支持
    sudo add-apt-repository ppa:deadsnakes/ppa -y
    sudo apt install python3.8
    

  还有一点,由于在之前在编译 U-Boot 时,我已经安装了大部分构建过程中需要的工具,因此,这里可能没有太多需要重新安装的工具了。如果出现错误,具体见博文 U-Boot 之一 零基础编译 U-Boot 过程详解、Image 镜像介绍及使用说明、DTB 文件使用说明 应该有解决方法。

运行环境

  我使用的嵌入式环境是 STM32F769I-EVAL 板子。STM32F769I-EVAL 板子使用的 STM32F769NI 这个 MCU,STM32F769NI 这款 MCU 采用的是 ARM Cortex-M7 的核心,指令集架构是 ARMv7m。此外,还需要注意,这个板子上的的串口的 RX 默认是断开,需要用短路帽连接起来。
在这里插入图片描述
  Kernel 本身没有提供对于 STM32F769I-EVAL 板子的支持,但是它支持的 STM32F769-Disco 板子。STM32F769-Disco 开发板与 STM32F769I-EVAL 评估板的 MCU 都使用的是 STM32F769NI,因此,我们直接编译后下载是可以运行的。

  不过两者的板载资源不同(例如,DRAM 大小),因此,Kernel 识别的某些外设信息是不对的。这里仅仅关注如何进行零基础编译,在博文 Linux 之十 移植过程详解、 STM32F769I-EVAL 开发板适配 中我详细介绍了如何将 Kernel 移植到 STM32F769I-EVAL 板子。

构建过程

  Linux Kernel 的构建过程与 U-Boot 构建过程是一模一样的。因为 U-Boot 的构建就是采用的 Linux Kernel 的 Kconfig/Kbuild 构建系统。具体见博文 U-Boot 之一 零基础编译 U-Boot 过程详解、Image 镜像介绍及使用说明、DTB 文件使用说明U-Boot 之四 构建过程(Kconfig 配置 + Kbuild 编译)详解

  在开始编译之前,我们先介绍两个命令:make clean 用于清空编译中间文件 和 make distclean 用于清除所有编译产生的文件。如果我们想要重新编译,可以使用以上两个命令清理环境。这两个命令均定义在 Kernel 源码根目录的 Makefile 中。

  1. 第一步肯定是获取 Kernel 的源代码,我这里采用的是最新的 LTS 版:5.15.52:wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.52.tar.xz。下载后直接解压:tar xvJf linux-5.15.52.tar.xz
    在这里插入图片描述

    1. 可能需要相关工具才可以直接从 Kernel 官网下载
    2. 最好不要选用 Mainline 分支的版本
    3. 解压过程时间较长,耐心等待

      成功下载并解压源代码之后,我们需要进入解压的 Kernel 源码目录下,使用命令:cd linux-5.15.52。此后就在 Kernel 源码目录下进行各种操作。

  2. 第二步就是生成配置,直接使用命令:ARCH=arm CROSS_COMPILE=arm-none-eabi- make O=build_stm32 stm32_defconfig其中,参数 O=xx 用于指定编译的输出路径,这样,所有编译输出都放到指定目录下,方便查看。
    在这里插入图片描述

  3. 第三步修改配置(裁剪)。直接使用命令:ARCH=arm CROSS_COMPILE=arm-none-eabi- make O=build_stm32 menuconfig其中,参数 O=xx 用于指定编译的输出路径,这样,所有编译输出都放到指定目录下,方便查看。
    在这里插入图片描述
      需要特殊注意的是,每一步命令都必须带有 ARCH=arm CROSS_COMPILE=arm-none-eabi- 这俩参数,否则不能正常按照我们指定的架构来显示配置。正常就会进入下面左侧的配置界面了。
    在这里插入图片描述
      这里我们需要更改一些参数: “System Type” > “DRAM Base” = 0xc0000000 and “DRAM Size” = 0x02000000,以及将 “Boot options” -> “Kernel Execute-In_Place From ROM” 前面的型号去掉。最后保存退出即可。
    在这里插入图片描述

  4. 第四步就是真正的编译了,直接使用命令:ARCH=arm CROSS_COMPILE=arm-none-eabi- make O=build_stm32 -j$(nproc),然后就是漫长的等待… 其中,参数 O=xx 用于指定编译的输出路径,这样,所有编译输出都放到指定目录下,方便查看。
    在这里插入图片描述

  5. 正常编译完成之后,就会在 Kernel 源码的对应架构目录下生成我们真正需要的 arch/arm/boot/zImagearch/arm/boot/dts/stm32f769-disco.dtb 这两个文件了。
    在这里插入图片描述

Image 说明

  在构建 Kernel 镜像时,我们可以为内核镜像选择多种格式。具体就看在 make menuconfig 这一步的配置。某些格式的镜像可能是针对特定架构的,下面来介绍一下常见的镜像格式。
在这里插入图片描述

  • vmlinuxvm 是 Virtual Memory 的缩写,它是由用户对内核源码编译出来的最原始的内核文件,是一个 ELF( Executable and Linkable Format)格式的文件。当我们编译完 Kernel 后,在源码根目录下就会生成该文件。vmlinux 是最原始的镜像文件,其他镜像文件都有它来生成。所有其他未压缩的镜像都直接或者间接由它生成。
    在这里插入图片描述
      vmlinux 这个名字来自于 Unix。在上世纪 60 年代,Unix 开发者简单地称他们的内核为 “unix”,所以,Linux 在上世纪 90 年代诞生后,Linus 开始称他们的内核为 “Linux”。当虚拟内存被开发为更容易的多任务处理能力时,“vm”被放在文件的前面,以表明内核支持虚拟内存。有一段时间,Linux 内核被称为 vmlinux。
      在 Ubuntu 中,内核镜像文件就是 vmlinux 这个格式,存储在 /boot 文件夹中,名字的格式为 vmlinuz-A.B.C.D。A.B.C 是 Stable Kernel 的版本号,D 表示 PATCH 版本号。
    在这里插入图片描述
    注意,在 ./arch/arm/boot/compressed 下,还有个经过压缩之后的 vmlinux 。所有其他与压缩有关的镜像都直接或者间接由它生成。
    在这里插入图片描述

  • System.map: 记录 Kernel 中各个符号在内核中位置,但是这个文件是使用了 nm 和 grep工具来手动生成的(不是链接器输出的 map 文件),具体处理是在 scripts\mksysmap 文件中!
    在这里插入图片描述

  • vmlinuz:随着内核变得越来越大,无法容纳到可用的引导内存中,因此压缩了内核镜像,命名上则将最后 x 改为 z,以表示它是用 zlib 压缩压缩的。这种压缩并不总是被使用,而是经常被 LZMA 或 BZIP2 取代。

  • Image:它是基于根目录下未压缩的 vmlinux 生成的,编译完 Kernel 后,在对应架构的 boot(例如,arch\arm\boot) 目下就会生成该文件。
    在这里插入图片描述
      它经过 objcopy 处理的只包含二进制数据的内核代码,就是将 vmlinux 中的 ELF 头和尾部的符号表等去掉之后剩余的内容。
    在这里插入图片描述

  • zImage:它是基于压缩过的 vmlinux 生成的,编译完 Kernel 后,在对应架构的 boot(例如,arch\arm\boot) 目下就会生成该文件。
    在这里插入图片描述
    生成方式与上面的 Image 是一模一样的(掐头去尾)。这里就不多上图了。

    1. zImage 是 ARM linux 常用的一种压缩镜像文件
    2. 这种格式的 Linux 镜像文件多存放在 NAND 上
  • bzImage:bz 表示 big zImage,其格式与 zImage 类似,但采用了不同的压缩算法,bzImage 的压缩率更高。

  • uImage:这是 uboot 专用的镜像文件,它是在 zImage 之前加上一个长度为 0x40 的头信息(tag),在头信息内说明了该镜像文件的类型、加载位置、生成时间、大小等信息。换句话说,若直接从 uImage 的 0x40 位置开始执行,则 zImage 和 uImage 没有任何区别。

    1. 编译完 Kernel 后,在对应架构的 boot(例如,arch\arm\boot) 目下就会生成该文件
    2. 这种格式的 Linux 镜像文件多存放在 NAND 上
  • xipImage:它是基于根目录下未压缩的 vmlinux 生成的,编译完 Kernel 后,在对应架构的 boot(例如,arch\arm\boot) 目下就会生成该文件。
    在这里插入图片描述
    生成方式与上面的 Image 是一模一样的(掐头去尾)。这里就不多上图了。

    xip 是 Execute In Place 的缩写,这种格式的 Linux 镜像文件多存放在 NorFlash 上,运行时不需要拷贝到内存 SDRAM 中,可以直接在 NorFlash 中运行。

Image 使用

  Kernel 镜像的使用与我们在 make menuconfig 中的配置是有关系的。当然也与 U-Boot 相关。在上面的编译中,我们实际需要的文件是 linux-5.15.52/build_stm32/arch/arm/boot/zImagelinux-5.15.52/build_stm32/arch/arm/dts/stm32f769-disco.dtb 这两个文件。

  • zImage:这个通常需要使用 U-Boot 的相关命令来将它下载开发板的对应存储器中。例如,我这里就需要使用命令 fatload mmc 0 0xc0000000 zImage 将 zImage 下载到 MMC 中
  • stm32f769-disco.dtb:这个需要直接烧录到 MCU 的内部 FLASH 中,地址是 0x81c0000。这个其实在 U-Boot 中有对应的配置项。

参考

  1. https://www.howtogeek.com/howto/31632/what-is-the-linux-kernel-and-what-does-it-do/
  2. https://www.cnblogs.com/Oude/articles/12039025.html
  3. https://james-hui.com/2021/07/02/building-a-small-uboot-linux-and-rootfs-for-arm-cortex-m7/
  4. https://blog.csdn.net/m0_47799526/article/details/106203517
  5. https://blog.csdn.net/lpwsw/article/details/122098847

猜你喜欢

转载自blog.csdn.net/ZCShouCSDN/article/details/122102706