之二 零基础构建流程、Image 介绍、Image 使用说明"/>
Linux Kernel 之二 零基础构建流程、Image 介绍、Image 使用说明
在上一篇博文 Linux Kernel 之一 内核架构、源码文件、API/ABI 介绍、FHS 中我们已经对 Linux Kernel 的源码进行了整体的一个认识,本篇博文就来看看如何对 Linux Kernel 进行编译,不过这里仅仅关注构建过程本身,不涉及 Linux Kernel 中任何技术细节。
基本环境
我们需要一台 Linux PC(可以使用虚拟机) 及一个块可运行 Linux 的开发板。尽管部分源码支持在 Windows 下的 Linux 环境中编译(例如 WSL),但是强烈不推荐,因为难免出现一些奇怪问题。
此外,如果你已经按照 U-Boot 系列博文 中配置好了基本环境,那么 Linux Kernel 系列博文中的有些章节内容可以略过。我这里仍然是按照全新安装的 Ubuntu 22.04 TLS 环境来进行学习!
开发环境
我这里使用的基本开发环境是全新安装的 Ubuntu 22.04.1 LTS,为了避免一开始就安装一堆不是道干啥的软件问题,我们采用在后续开发中缺少补啥的方式来进行后续博文学习。使用 sudo apt install lsb-core
安装并查看发行版基本信息如下:
- 新版(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
运行环境
我使用的嵌入式 Linux 运行环境是 STM32F769I-EVAL 板子。STM32F769I-EVAL 板子使用的 STM32F769NI 这个 MCU,STM32F769NI 这款 MCU 采用的是 ARM Cortex-M7 的核心,指令集架构是 ARMv7m。此外,还需要注意,这个板子上的的串口的 RX 默认是断开,需要用短路帽连接起来。
Linux Kernel 本身没有提供对于 STM32F769I-EVAL 板子的支持,但是它支持的 STM32F769-Disco 板子。STM32F769-Disco 开发板与 STM32F769I-EVAL 评估板的 MCU 都使用的是 STM32F769NI,因此,本文我们就直接编译 STM32F769-Disco 板子的固件。如下是整个嵌入式 Linux 环境的内存布局。
不过两者的板载资源不同(例如,DRAM 大小),因此,直接下载到开发板运行则 U-Boot 识别的某些外设信息是不对的。本篇博文仅仅关注如何进行零基础编译,在博文 Linux Kernel 之三 移植过程详解、 STM32F769I-EVAL 开发板适配 中我详细介绍了如何将 Linux Kernel 移植到 STM32F769I-EVAL 板子。
示例代码
嵌入式 Linux 运行环境搭建系列博文涉及的所有源代码均放到了我个人的 Gitee 上:。这个仓库中包含了的所有源代码会根据后续博文一步步进行各种适配,如果你是纯学习则可以直接 clone 该仓库来学习!
其中,为了在适配 Buildroot、Yocto 等工具时方便,该仓库采用了 git submodule
来进行基本的组织。本篇博文我们重点是学习 Linux Kernel,因此,也可以直接使用 .15.52 这个子仓库来学习。
构建过程
这里我们仅仅关注零基础编译过程,在编译过程中出现啥错误在去重点解决。首先我们需要先安装的就是 Arm GNU Toolchain 11.3.Rel1 编译工具链,具体步骤如下:
-
安装 GNU Arm Embedded Toolchain。这里有个需要注意的点,如果大家搜索 Ubuntu 下安装 GCC for ARM,很多文章都过推荐使用 apt 命令来安装,类似于:
sudo apt-get install gcc-arm-none-eabi
,这个版本并不是最新的(貌似使用这个旧版本也可以,我选择了使用最新版)。
更重要的是,ARM 之前已经宣布不再更新 Launchpad 上的 GCC for ARM 了(具体见 Launchpad 上的说明),现在只在 ARM 的官网提供编译好的压缩包及源代码的压缩包。这里简单来讲解一下直接从 ARM 官网下载压缩包的安装方法:- 从 下载编译工具链压缩包,然后解压。我这里将其解压到了
/opt/
目录下。直接使用命令:sudo tar xvf arm-gnu-toolchain-11.3.rel1-x86_64-arm-none-eabi.tar.xz -C /opt
等待解压完成。
- 此时我们需要将工具链添加到系统环境变量这样才能正常在终端中使用各种命令。具体有两种方法(我采用了第二种):
-
第一种是创建符号连接
sudo ln -s /opt/gcc-arm-none-eabi-your-version/bin/arm-none-eabi-gcc /usr/bin/arm-none-eabi-gcc sudo ln -s /opt/gcc-arm-none-eabi-your-version/bin/arm-none-eabi-g++ /usr/bin/arm-none-eabi-g++ sudo ln -s /opt/gcc-arm-none-eabi-your-version/bin/arm-none-eabi-gdb /usr/bin/arm-none-eabi-gdb sudo ln -s /opt/gcc-arm-none-eabi-your-version/bin/arm-none-eabi-size /usr/bin/arm-none-eabi-size
-
第二种是在
.bashrc
文件(或者是/etc/profile
)最后面增加以下内容:# GCC for ARM export PATH="/opt/arm-gnu-toolchain-11.3.rel1-x86_64-arm-none-eabi/bin:$PATH"
可能需要重启我们之前已经打开的终端以上配置才会生效。
-
- 通过压缩包安装不能解决依赖关系,需要我们自己运行尝试,看看少啥安啥。目前已知的依赖是 ncures5(会报错:error while loading shared libraries: libncurses.so.5)和 libncursesw5(报错:error while loading shared libraries: libncursesw.so.5: cannot open shared object file: No such file or directory),其需要安装
sudo apt install libncurses5 libncursesw5
。 - 新版(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
- 从 下载编译工具链压缩包,然后解压。我这里将其解压到了
-
获取 Kernel 的源代码。我这里采用的是最新的 LTS 版:5.15.52:
wget .x/linux-5.15.52.tar.xz
。下载后直接解压:tar xvJf linux-5.15.52.tar.xz
。如果是自己学习,也可以直接使用我的git clone .git
- 可能需要相关工具才可以直接从 Kernel 官网下载
- 最好不要选用 Mainline 分支的版本
- 解压过程时间较长,耐心等待
成功下载并解压源代码之后,我们需要进入解压的 Kernel 源码目录下,使用命令:
cd linux-5.15.52
。此后就在 Kernel 源码目录下进行各种操作。 -
生成配置。直接使用命令:
ARCH=arm CROSS_COMPILE=arm-none-eabi- make O=build_stm32 stm32_defconfig
。其中,参数O=xx
用于指定编译的输出路径,这样,所有编译输出都放到指定目录下,方便查看。 不出意外的话会出现以下错误:
1./bin/sh: 1: bison: not found
这个错误是由于我们没有安装 bison 这个工具。Bison 是一个通用解析器生成器。GUN 软件之一,官网 /。Ubuntu 下直接使用命令:sudo apt install bison
即可(这个不是最新版,如果需要最新版需要自己从源码安装)。
2./bin/sh: 1: flex: not found
这个错误是由于我们没有安装 flex 这个工具。flex 是一个词法分析器。用来将一个 .l 文件生成一个 .c 程序文件。源代码托管于 Github:。Ubuntu 下直接使用命令:sudo apt install flex
即可。
3. 正常执行命令后如下图所示
-
修改配置(裁剪)。直接使用命令:
ARCH=arm CROSS_COMPILE=arm-none-eabi- make O=build_stm32 menuconfig
。其中,参数O=xx
用于指定编译的输出路径,这样,所有编译输出都放到指定目录下,方便查看。- Unable to find the ncurses package
这个错误是由于我们没有安装 ncurses 这个工具。ncurses(new curses)是一套编程库,它提供了一系列的函数以便使用者调用它们去生成基于文本的用户界面。GUN 软件之一,官网:/。Ubuntu 下直接使用命令:sudo apt install libncurses-dev
即可。
- 安装成功之后,重新执行命令,就会进入下面的界面:
这里我们需要更改一些参数:System Type > DRAM Base = 0xc0000000 and DRAM Size = 0x02000000
,以及将Boot options -> Kernel Execute-In_Place From ROM
前面的型号去掉。最后保存退出即可。
需要特殊注意的是,每一步命令都必须带有ARCH=arm CROSS_COMPILE=arm-none-eabi-
这俩参数,否则不能正常按照我们指定的架构来显示配置。正常就会进入下面左侧的配置界面了。
- Unable to find the ncurses package
-
最后就是真正的编译了。直接使用命令:
ARCH=arm CROSS_COMPILE=arm-none-eabi- make O=build_stm32 -j$(nproc)
,然后就是漫长的等待… 其中,参数O=xx
用于指定编译的输出路径,这样,所有编译输出都放到指定目录下,方便查看。
-
正常编译完成之后,就会在 Kernel 源码的对应架构目录下生成我们真正需要的
arch/arm/boot/zImage
和arch/arm/boot/dts/stm32f769-disco.dtb
这两个文件了。
Image 说明
在构建 Kernel 镜像时,我们可以为内核镜像选择多种格式。具体就看在 make menuconfig
这一步的配置。某些格式的镜像可能是针对特定架构的,下面来介绍一下常见的镜像格式。
-
vmlinux:
vm
是 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 是一模一样的(掐头去尾)。这里就不多上图了。- zImage 是 ARM linux 常用的一种压缩镜像文件
- 这种格式的 Linux 镜像文件多存放在 NAND 上
-
bzImage:bz 表示 big zImage,其格式与 zImage 类似,但采用了不同的压缩算法,bzImage 的压缩率更高。
-
uImage:这是 uboot 专用的镜像文件,它是在 zImage 之前加上一个长度为 0x40 的头信息(tag),在头信息内说明了该镜像文件的类型、加载位置、生成时间、大小等信息。换句话说,若直接从 uImage 的 0x40 位置开始执行,则 zImage 和 uImage 没有任何区别。
- 编译完 Kernel 后,在对应架构的
boot
(例如,arch\arm\boot
) 目下就会生成该文件 - 这种格式的 Linux 镜像文件多存放在 NAND 上
- 编译完 Kernel 后,在对应架构的
-
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/zImage
和 linux-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 中有对应的配置项。
参考
- /
- .html
- /
更多推荐
Linux Kernel 之二 零基础构建流程、Image 介绍、Image 使用说明
发布评论