2. ARM平台与Linux内核基础
好,咱们正式开始动手。这一章我带你搞定内核源码的获取、版本管理,以及最基础的配置体系。说白了,就是先把“食材”准备好,再搞懂“厨房”的结构。
2.1 Linux内核版本管理——别选错了分支
Linux内核版本号,看着简单,其实有门道。比如 5.10.168:
- 5 —— 主版本号,大改版才变
- 10 —— 次版本号,以前偶数表示稳定版,奇数表示开发版。现在这个规则弱化了,但很多老项目还在用
- 168 —— 补丁级别,也就是小修小补
我个人习惯,做产品选型时,优先选 LTS(长期支持) 版本。比如 5.10、5.15、6.1 这些。为什么?因为社区会维护好几年,你不需要频繁跟着主线升级。
怎么看一个版本是不是LTS?去 kernel.org 看,LTS版本会有黄色标签。或者用命令:
git tag -l 'v5.10.*' | tail -5
2.2 获取源码:git clone vs tarball下载
两种方式,各有各的适用场景。我列个表,你一看就明白:
| 方式 | 命令/操作 | 适用场景 | 大小 |
|---|---|---|---|
| git clone | git clone --depth=1 git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git |
需要跟踪历史、切换分支、打补丁 | ~2GB(完整) / ~200MB(浅克隆) |
| tarball下载 | wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.168.tar.xz |
只需要固定版本,不关心历史 | ~150MB(压缩) / ~1GB(解压后) |
git clone 的坑: 完整克隆整个内核仓库,大概2GB,网速慢的话能等一两个小时。我建议用 --depth=1 做浅克隆,只拉取最新代码。如果你后面需要切换分支,再用 git fetch --unshallow 补全历史。
tarball 的坑: 下载完记得校验签名。内核社区提供 .tar.sign 文件,用 gpg --verify 验证。我曾经遇到过下载的包被中间人篡改,编译出来的内核有后门。嗯,从那以后,我每次下载都校验。
# 下载并校验
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.168.tar.xz
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.168.tar.sign
xz -cd linux-5.10.168.tar.xz | gpg --verify linux-5.10.168.tar.sign -
2.3 内核源码目录结构解析——别迷路
解压完源码,进去一看,几十个目录。别慌,核心目录其实就这几个:
做ARM平台移植,你打交道最多的就是 arch/arm/ 和 arch/arm64/。里面按芯片厂商分目录:mach-* 是板级代码,plat-* 是平台通用代码。
drivers/ 里你用不到的驱动,以及 arch/ 里不需要的板级支持。每删一个,镜像就小一点,启动就快一点。
2.4 Kconfig与Makefile入门——配置系统的骨架
内核的配置系统,核心就两个文件:Kconfig 和 Makefile。它们成对出现,几乎每个子目录都有。
2.4.1 Kconfig——定义“有什么可以选”
Kconfig 文件定义了配置选项。比如 drivers/net/Kconfig 里的一段:
config DM9000
tristate "DM9000 support"
depends on ARM || MIPS
select CRC32
help
Support for DM9000 series Ethernet controllers.
Say Y here if you have such a device.
解释一下:
- config DM9000 —— 选项名,最终会变成
CONFIG_DM9000 - tristate —— 三态:N(不编译)、Y(编译进内核)、M(编译成模块)
- depends on —— 依赖条件,只有ARM或MIPS架构才显示这个选项
- select —— 反向依赖,选了DM9000就自动选上CRC32
我个人习惯,写驱动时一定把 depends on 写清楚。不然别人在x86上配你的驱动,编译报错还找不到原因。
2.4.2 Makefile——定义“怎么编译”
同目录下的 Makefile 告诉内核怎么编译:
obj-$(CONFIG_DM9000) += dm9000.o
这行意思是:如果 CONFIG_DM9000 的值是 y,就把 dm9000.c 编译进内核;如果是 m,就编译成 dm9000.ko 模块;如果是 n,就不编译。
obj-y += dm9000.o,结果这个驱动不管配置选没选,都被强制编译进内核。后来查了半天才发现是少写了 $(CONFIG_...) 条件。
2.4.3 配置操作流程
搞懂了Kconfig和Makefile,配置操作就简单了:
- 生成默认配置:
make ARCH=arm defconfig—— 生成一个基于当前架构的默认配置 - 菜单配置:
make ARCH=arm menuconfig—— 图形化菜单,按空格切换Y/M/N - 保存配置: 退出时自动保存到
.config文件 - 查看最终配置:
grep CONFIG_DM9000 .config
你想想看,整个配置系统就像一个巨大的开关面板。Kconfig 定义了每个开关的标签和依赖关系,Makefile 定义了每个开关控制哪盏灯。你通过 menuconfig 拨动开关,最终生成一个 .config 文件来记录所有开关状态。
CONFIG_DM9000=y,但它的依赖 CONFIG_CRC32 没开,编译就会报错。用 make menuconfig 会自动处理这些依赖。