Skip to content

LV030-NXP的linux内核

一、NXP 源码获取

1. 源码版本

1.1 ALPHA-I.MX6ULL 开发板

我使用的是 ALPHA-I.MX6ULL 开发板,正点原子官方有维护一个版本 linux 内核,可以直接使用的,正点原子使用的 NXP 的 内核应该 Release rel_imx_4.1.15_2.1.0_ga: MLK-14762 ARM: dts: imx6sll-evk: correct gpio pin for lcd power control · nxp-imx/linux-imx · GitHub 这个版本,这个版本的基础版本是 4.1.15,在 Makefile 中有说明:linux-imx/Makefile

1.2 自定义一个

由于前面 uboot 升级到了 2019.04,我们这里 linux 内核也选择一个同年份的,这里我选了 4.19.71,主要是它的版本大于 4.4 支持设备树插件,也有利于后续的学习。

这个版本对于仓库来说还有有些老了,tag 地址比较好找:Release v4.19.71 · nxp-imx/linux-imx (github.com),好像还有个类似分支的地址是 nxp-imx/linux-imx at v4.19.71 (github.com)

image-20260113123358505

可以看到提交记录是:e7d2672c66e4d3675570369bf20856296da312c4。我们点开这个 tag 就能看到所在的分支:

image-20260113123440539

它所在分支地址:nxp-imx/linux-imx at git.kernel.org/linux-stable/linux-4.19.y (github.com),不过有 tag 的话我们一般还是使用 tag 来下载源码。

2. 源码获取

我们通过以下命令下载源码:

shell
git clone -b v4.19.71 --depth=1 https://github.com/nxp-imx/linux-imx.git
git clone -b v4.19.71 --depth=1 git@github.com:nxp-imx/linux-imx.git

这个仓库比较大,我就没放到自己的 gitee 上了,只是存了一些修改的文件。不过在 windows 下使用 git 下载似乎有坑,自动 checkout 的时候失败了,说是几个文件路径无效,在linux下似乎没有问题,要是有问题我们可以直接下载对应的 zip 压缩包,都是一样的。在linux下通过git下载,会有如下打印信息:

shell
Cloning into 'linux-imx'...
remote: Enumerating objects: 65570, done.
remote: Counting objects: 100% (65570/65570), done.
remote: Compressing objects: 100% (63001/63001), done.
remote: Total 65570 (delta 5072), reused 18939 (delta 1655), pack-reused 0 (from 0)
Receiving objects: 100% (65570/65570), 175.73 MiB | 1.13 MiB/s, done.
Resolving deltas: 100% (5072/5072), done.
Note: checking out 'e7d2672c66e4d3675570369bf20856296da312c4'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

  git checkout -b <new-branch-name>

      1 [main] git 1833 C:\Users\HUANGK~1\AppData\Local\Temp\Mxt221\bin\git.exe: *** fatal error - crealloc would have returned NULL
Hangup

这个时候是出于一个游离分支,我们可以通过下面的命令创建并切换分支:

shell
git checkout -b main

下载压缩包的话大概192MB,解压后是909MB:

image-20260113140711404

二、编译源码

1. 安装依赖库

貌似前面 uboot 已经装过了,通用的,这里就不用了。我在野火的教程中看到需要安装这些:

shell
sudo apt-get install lzop libncurses5-dev

这个 lzop 要装,不然会报错:

image-20241102171357088

2. 编译源码

2.1 清理工程文件

shell
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- clean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean

这里在回顾一下吧:

clean 命令主要清除的是编译过程中生成的可执行文件以及配置文件。这些文件是具体编译任务的产物,例如.exe 文件、.o 文件等。清除这些文件有助于保持构建环境的干净,避免可能因残留文件导致的问题。然而,make clean 并不会清除其他一些可能存在的文件,如日志文件、中间编译结果等,这使得它在彻底性上有所不足。

make distclean 命令则更为彻底。它不仅清除可执行文件和配置文件,还会清除所有由 make 命令生成的文件。这包括了编译过程中产生的中间文件、日志文件、缓存文件等。通过执行 make distclean,我们能够确保构建环境的完全清洁,为后续的构建过程提供一个干净、无残留的环境。然而,这也意味着清除了一些可能需要保留的文件,比如版本控制系统的缓存文件等。

不过吧,由于有的时候我们在图形界面配置完 linux 内核,可能会忘记保存 config 文件,这个时候用 distclean 会把配置文件一起删了,这样其实是没有必要的,所有后面我基本用的都是 clean 命令,需要重新配置的话再用 distclean。

2.2 配置工程

shell
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx_v6_v7_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig

2.3 编译源码

shell
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- all -j16

这个命令中 all 表示编译所有内容,包括设备树和内核镜像。不出意外的话编译完是这样的:

image-20241102171750221

Linux 内核编译完成以后会在 arch/arm/boot 目录下生成 zImage 镜像文件,如果使用设备树的话还会在 arch/arm/boot/dts 目录下开发板对应的.dtb(设备树)文件,比如 imx6ull-14x14-evk.dtb 就是 NXP 官方的 I.MX6ULL EVK 开发板对应的设备树文件。至此我们得到两个文件:

(1)Linux 内核镜像文件: zImage。

(2)NXP 官方 I.MX6ULL EVK 开发板对应的设备树文件: imx6ull-14x14-evk.dtb。

3. uImage 与 zImage

在之前的一些老一点的版本内核中,我们有可能会见到 uImage,这里我们可以看到编译出来的是 zImage,他们有什么区别?我们先来看一下成果物目录,我们根据编译信息,可以知道内核相关的成果物是在 arch/arm/boot 这个目录下的:

image-20241117140433775

内核编译(make)之后会生成两个文件,一个 Image,一个 zImage,其中 Image 为内核映像文件,而 zImage 为内核的一种映像压缩文件,从图中可以看出 Image 大约为 18M,而 zImage 大概是 8M。

linux 内核经过编译后会生成一个 elf 格式的可执行程序,叫 vmlinux 或 vmlinuz,这个就是原始的未经任何处理加工的原版内核 elf 文件:

image-20241117141034327

嵌入式系统部署时烧录的一般不是这个 vmlinuz/vmlinux,而是要用 objcopy 工具去制作成烧录镜像格式,经过制作加工后的烧录镜像文件就叫 Image。原则上 Image 就可以直接被烧录到 Flash 上进行启动执行(类似于 u-boot.bin),但是实际上并不是这么简单。linux 的大佬们觉得 Image 还是太大了!!所以对 Image 进行了压缩,并且在 image 压缩后的文件的前端附加了一部分解压缩代码,构成了一个压缩格式的镜像就叫 zImage。运行的时候,通过 zImage 镜像头部的解压缩代码进行自解压,然后执行解压出来的内核镜像。

那么 uImage 又是什么的?它是 uboot 专用的映像文件,它是在 zImage 之前加上一个长度为 64 字节的“头”,说明这个内核的版本、加载位置、生成时间、大小等信息;其 0x40 之后与 zImage 没区别。

有些 uboot 是支持 zImage 启动的,有些则不支持。但是所有的 uboot 肯定都支持 uImage 启动(常用方式,有更多的优点)。

注意:uImage 不关 linux 内核的事,linux 内核只管生成 zImage 即可,这个加工过程其实就是在 zImage 前面加上 64 字节的 uImage 的头信息即可。

4. 只编译内核

linux 内核源码可以编译内核和设备树,这两个都有单独的命令可以进行编译,单独编译内核我们可以执行:

shell
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- clean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx_v6_v7_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zImage -j16
image-20241117142509551

会发现这样只会生成内核的镜像。

5. 编译设备树

编译设备树也有单独的命令:

shell
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- clean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx_v6_v7_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs -j16
image-20241117142741537

发现没有生成内核镜像文件,但是有一堆的设备树文件了。那能不能单独编译我指定的设备树文件?

shell
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- clean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx_v6_v7_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx6ull-14x14-evk.dtb -j16
image-20241117143010638

发现最后就只生成了这一个设备树文件。

6. 报错处理

6.1 gcc 版本过高问题

不出意外的话,最先报的是这个问题,当然,使用在 ubuntu20.04 中使用 4.19.71 版本内核应该是没有报,一些低版本的内核的可能会报,下图的报错应该是之前编译 4.1.15 版本的时候出现的:

image-20260113124228224

经查询,好像是 gcc 编译器 10 及以上版本才会出现问题,可以修改 linux 内核源码文件,我们在 linux 内核源码中 scripts/dtc 目录下的 dtc-lexer.lex.c_shipped 文件中找到 YYLTYPE yyloc 这一行:

image-20260113124658394

可以看到在 640 行,我们在这个之前面加上 extern :

c
extern YYLTYPE yylloc;

然后就可以正常编译啦。

6.2 缺少 lzop 库

编译过程中可能会有下边的报错提示:

image-20241102171357088

怎么解决?我们需要安装一下 lzop 库:

shell
sudo apt-get install lzop

然后再编译应该就可以了。

三、源码目录简介

我们编译完的linux内核源码的顶层目录将会有以下文件及目录:

image-20260113141458945

部分目录和文件说明如下:

类型名称描述备注
文件夹arch这个目录是和架构有关的目录,比如 arm、 arm64、 avr32、 x86 等等架构。每种架构都对应一个目录,在这些目录中又有很多子目录,比如 boot、 common、 configs 等等。以 arch/arm 为例,其子目录如图
image-20260113142800359
这些子目录用于控制系统引导、系统调用、动态调频、主频设置等。

(1)这里我们看一下arch/arm/configs,这个目录是不同平台的默认配置文件: xxx_defconfig
image-20260113142927789
在 arch/arm/configs 中就包含有NXP官方的 IMX6ULL EVK 评估板的默认配置文件: imx_v6_v7_defconfig,执行“make imx_v6_v7_defconfig”即可完成配置。
(2)arch/arm/boot/dts 目录里面是对应开发平台的设备树文件:
image-20260113143430869
(3)arch/arm/boot 目录下会保存编译出来的 Image 和 zImage 镜像文件,而 zImage 就是我们要用的 linux 镜像文件。
image-20260113143541196
(4)arch/arm/mach-xxx 目录分别为相应平台的驱动和初始化文件,比如 mach-imx 目录里面就是 I.MX 系列 CPU 的驱动和初始化文件。
Linux 自带
blockblock 是 Linux 下块设备目录,像 SD 卡、 EMMC、 NAND、硬盘等存储设备就属于块设备,block 目录中存放着管理块设备的相关文件。
cryptocrypto 目录里面存放着加密文件,比如常见的 crc、 crc32、 md4、 md5、 hash 等加密算法。
Documentation此目录里面存放着 Linux 相关的文档,如果要想了解 Linux 某个功能模块或驱动架构的功能,就可以在 Documentation 目录中查找有没有对应的文档。
drivers驱动目录文件,此目录根据驱动类型的不同,分门别类进行整理,比如 drivers/i2c 就是 I2C相关驱动目录, drivers/gpio 就是 GPIO 相关的驱动目录,这是我们学习的重点。
firmeare固件相关目录。
fs此目录存放文件系统,比如 fs/ext2、 fs/ext4、 fs/f2fs 等,分别是 ext2、 ext4 和 f2fs 等文件系统。
include头文件相关目录。
init初始化相关目录。
ipc进程间通信相关目录。
kernel内核相关目录。
lib库相关目录。
mm内存管理相关目录。
net网络相关目录。
samples例程相关目录。
scripts脚本相关目录。
security安全相关目录。
sound音频处理相关目录。
tools工具相关目录。
usr与 initramfs 相关的目录,用于生成initramfs。
virt提供虚拟机技术(KVM)。
文件.configLinux 最终使用的配置文件。编译生成的文件
.gitignoregit 工具相关文件。Linux 自带
.mailmap邮件列表。
.missing-syscalls.d 未知编译生成的文件
.tmp_xx这是一系列的文件,作用还不是很清楚。
.version和版本相关,具体没管。
.vmlinux.cmdcmd 文件,用于连接生成 vmlinux。
COPYING版权声明。Linux 自带
CREDITSLinux 贡献者。
KbuildMakefile 会读取此文件。
Kconfig图形化配置界面的配置文件。
MAINTAINERS维护者名单。
MakefileLinux 顶层 Makefile
Module.xx一系列文件,和模块有关。编译生成的文件
modules.xx"
READMELinux 描述文件。Linux 自带
REPORTING-BUGSBUG 上报指南
System.map符号表。编译生成的文件
vmlinux编译出来的、未压缩的 ELF 格式 Linux 文件
vmlinux.o编译出来的 vmlinux.o 文件。

四、VScode工程

还和之前一样,创建一个VScode工程,方便管理工程,工程工作区文件内容如下:

json
{
	"folders": [
		{
			"path": "."
		}
	],
	"settings": {
		// 搜索时不想显示的文件可按以下格式屏蔽(为 true 时屏蔽)
		"search.exclude": {
			"**/node_modules": true,
			"**/bower_components": true,
			"**/*.o":true,
			"**/*.su":true,
			"**/*.cmd":true,
			"Documentation":true,
		
			/* 屏蔽不用的架构相关的文件 */
			"arch/alpha":true,
			"arch/arc":true,
			"arch/arm64":true,
			"arch/avr32":true,
			"arch/[b-z]*":true,
			"arch/arm/plat*":true,
			"arch/arm/mach-[a-h]*":true,
			"arch/arm/mach-[n-z]*":true,
			"arch/arm/mach-i[n-z]*":true,
			"arch/arm/mach-m[e-v]*":true,
			"arch/arm/mach-k*":true,
			"arch/arm/mach-l*":true,
		
			/* 屏蔽排除不用的配置文件 */
			"arch/arm/configs/[a-h]*":true,
			"arch/arm/configs/[j-z]*":true,
			"arch/arm/configs/imo*":true,
			"arch/arm/configs/in*":true,
			"arch/arm/configs/io*":true,
			"arch/arm/configs/ix*":true,
			/* 屏蔽掉不用的 DTB 文件 */
			"arch/arm/boot/dts/[a-h]*":true,
			"arch/arm/boot/dts/[k-z]*":true,
			"arch/arm/boot/dts/in*":true,
			"arch/arm/boot/dts/imx1*":true,
			"arch/arm/boot/dts/imx7*":true,
			"arch/arm/boot/dts/imx2*":true,
			"arch/arm/boot/dts/imx3*":true,
			"arch/arm/boot/dts/imx5*":true,
			"arch/arm/boot/dts/imx6d*":true,
			"arch/arm/boot/dts/imx6q*":true,
			"arch/arm/boot/dts/imx6s*":true,
			"arch/arm/boot/dts/imx6ul-*":true,
			"arch/arm/boot/dts/imx6ull-9x9*":true,
			"arch/arm/boot/dts/imx6ull-14x14-ddr*":true,
		},
		// 不想在工作区显示的文件可按以下格式屏蔽(为 true 时屏蔽)
		"files.exclude": {
			"**/.git": true,
			"**/.svn": true,
			"**/.hg": true,
			"**/CVS": true,
			"**/.DS_Store": true,
			"**/*.o":true,
			"**/*.su":true,
			"**/*.cmd":true,
			"Documentation":true,
		
			/* 屏蔽不用的架构相关的文件 */
			"arch/alpha":true,
			"arch/arc":true,
			"arch/arm64":true,
			"arch/avr32":true,
			"arch/[b-z]*":true,
			"arch/arm/plat*":true,
			"arch/arm/mach-[a-h]*":true,
			"arch/arm/mach-[n-z]*":true,
			"arch/arm/mach-i[n-z]*":true,
			"arch/arm/mach-m[e-v]*":true,
			"arch/arm/mach-k*":true,
			"arch/arm/mach-l*":true,
		
			/* 屏蔽排除不用的配置文件 */
			"arch/arm/configs/[a-h]*":true,
			"arch/arm/configs/[j-z]*":true,
			"arch/arm/configs/imo*":true,
			"arch/arm/configs/in*":true,
			"arch/arm/configs/io*":true,
			"arch/arm/configs/ix*":true,
		
			/* 屏蔽掉不用的 DTB 文件 */
			"arch/arm/boot/dts/[a-h]*":true,
			"arch/arm/boot/dts/[k-z]*":true,
			"arch/arm/boot/dts/in*":true,
			"arch/arm/boot/dts/imx1*":true,
			"arch/arm/boot/dts/imx7*":true,
			"arch/arm/boot/dts/imx2*":true,
			"arch/arm/boot/dts/imx3*":true,
			"arch/arm/boot/dts/imx5*":true,
			"arch/arm/boot/dts/imx6d*":true,
			"arch/arm/boot/dts/imx6q*":true,
			"arch/arm/boot/dts/imx6s*":true,
			"arch/arm/boot/dts/imx6ul-*":true,
			"arch/arm/boot/dts/imx6ull-9x9*":true,
			"arch/arm/boot/dts/imx6ull-14x14-ddr*":true,
		},
	}
}