第4章:Linux基础入门:内核裁剪与编译、设备树配置、根文件系统构建
好,咱们进入正题。这一章讲的是Linux基础入门,但别小看“基础”这两个字。内核裁剪、设备树、根文件系统——这三样东西,说白了就是嵌入式Linux的“三驾马车”。你跑不起来,多半是其中一环出了问题。
我个人习惯把这三件事分开讲,但心里要清楚:它们是联动的。内核配错了,设备树再对也没用;根文件系统缺个库,应用就跑不起来。嗯,咱们一个一个来。
4.1 Linux内核裁剪与编译
内核裁剪,说白了就是“瘦身”。你想想看,一个通用Linux内核有好几万个驱动,你的板子上可能只用到了几十个。剩下的全是累赘。
我在项目中遇到过一块Flash只有16MB的板子,默认内核编译出来快30MB。根本烧不进去。后来裁剪到4MB,跑得稳稳的。
4.1.1 获取内核源码
先从官网或者你的芯片厂商那里拿到内核源码。我建议用长期支持版(LTS),稳定。
# 以Linux 5.10 LTS为例
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.100.tar.xz
tar -xf linux-5.10.100.tar.xz
cd linux-5.10.100
4.1.2 配置内核
配置内核有几种方式。我个人最常用的是menuconfig,图形界面,直观。
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
进去之后,你会看到一堆菜单。别慌。核心思路是:
- 关掉不需要的驱动:比如你的板子没有WiFi,就把无线网络驱动全关了。
- 保留必要的文件系统支持:ext4、squashfs、initramfs这些,看你的根文件系统类型。
- 开启调试选项:初期开发阶段,建议打开
Kernel hacking里的printk和early printk。
4.1.3 编译内核
配置好了,直接编译。注意指定架构和交叉编译器。
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4 zImage
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4 modules
这里-j4是并行编译,看你CPU核心数。我习惯用-j$(nproc)自动检测。
编译完成后,你会得到arch/arm/boot/zImage。这就是你的内核镜像。
Image而不是zImage。别搞混了,否则启动时会直接卡住。
4.2 设备树配置
设备树(Device Tree),说白了就是告诉内核“你的板子上有什么硬件”。以前老内核用板级文件(board file),硬编码。现在都改用设备树了,灵活得多。
我记得第一次接触设备树时,觉得它就是个“硬件描述语言”。其实没那么玄乎,就是个树形结构,描述CPU、内存、外设的地址和中断。
4.2.1 设备树的基本结构
一个典型的设备树文件(.dts)长这样:
/dts-v1/;
/ {
model = "My Custom Board";
compatible = "vendor,myboard";
chosen {
stdout-path = &uart0;
};
memory@80000000 {
device_type = "memory";
reg = <0x80000000 0x10000000>; // 256MB
};
uart0: serial@10000000 {
compatible = "ns16550";
reg = <0x10000000 0x1000>;
interrupts = <0 20 4>;
clock-frequency = <24000000>;
};
};
看到没?每个节点就是一个硬件设备。属性里写的是它的配置参数。
4.2.2 编译设备树
设备树源文件(.dts)需要编译成二进制(.dtb),内核才能用。
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- myboard.dtb
或者手动用dtc工具:
dtc -I dts -O dtb -o myboard.dtb myboard.dts
compatible属性必须和内核驱动里的匹配。否则驱动不会加载。我踩过这个坑,明明驱动写好了,就是没反应,最后发现是compatible拼写错了。
4.2.3 设备树覆盖
有时候你需要动态修改设备树,比如接一个扩展板。这时候可以用设备树覆盖(overlay)。
我个人觉得这个功能在调试阶段特别有用。不用重新编译整个内核,改个.dts文件,加载一下就行。
4.3 根文件系统构建
根文件系统,就是Linux启动后挂载的“/”目录。它里面要有init程序、库文件、配置文件、应用等等。
构建根文件系统,主流工具有两个:Buildroot 和 Yocto。我两个都用过,说说我的感受。
4.3.1 Buildroot:轻量级首选
Buildroot适合做小系统。配置简单,编译快。我做一个简单的IoT网关,用Buildroot半小时就能搞定。
基本用法:
git clone https://git.buildroot.net/buildroot
cd buildroot
make menuconfig
在菜单里选择:
- Target architecture:ARM
- Toolchain:用外部工具链或Buildroot自己编译
- Filesystem images:选ext4或squashfs
然后:
make
编译完成后,output/images/下就有根文件系统镜像了。
4.3.2 Yocto:大型项目的选择
Yocto功能更强大,但学习曲线也陡。它用BitBake作为构建引擎,支持复杂的依赖关系和定制化。
我参与过一个工业控制项目,用了Yocto。因为它需要支持多种硬件平台、多种软件栈,Yocto的层(layer)机制特别适合这种场景。
基本步骤:
git clone git://git.yoctoproject.org/poky
cd poky
source oe-init-build-env
bitbake core-image-minimal
Yocto的配置文件在conf/local.conf里。你可以指定目标架构、包类型、调试选项等。
| 特性 | Buildroot | Yocto |
|---|---|---|
| 编译速度 | 快 | 慢(第一次可能几小时) |
| 定制化程度 | 中等 | 高 |
| 学习曲线 | 平缓 | 陡峭 |
| 适用场景 | 小系统、原型验证 | 大型产品、多平台 |
4.3.3 手动构建根文件系统
如果你想彻底搞懂根文件系统,可以手动构建。虽然麻烦,但能学到东西。
基本步骤:
- 创建目录结构:
bin,sbin,etc,lib,usr,dev,proc,sys - 复制BusyBox到
bin/,它提供基本的shell和命令 - 复制交叉编译的库文件到
lib/ - 创建
/etc/inittab和/etc/fstab - 创建设备节点:
mknod /dev/console c 5 1
嗯,手动构建确实繁琐。我建议新手先用Buildroot,等熟悉了再尝试手动。
ld-linux.so,结果所有动态链接的程序都跑不起来。检查了半天才发现。所以,lib/目录一定要完整。
4.4 三者的整合
内核、设备树、根文件系统,这三者最终要整合在一起,才能启动系统。
启动流程大致是:
- Bootloader(如U-Boot)加载内核和设备树到内存
- 内核解析设备树,初始化硬件
- 内核挂载根文件系统,启动init进程
所以,你在开发时要确保:
- 内核配置正确(驱动、文件系统支持)
- 设备树描述准确(地址、中断、时钟)
- 根文件系统完整(init、库、应用)
任何一个环节出问题,系统都起不来。调试时,我习惯先看内核启动日志(通过串口),看它卡在哪一步。是设备树解析失败?还是根文件系统挂载不上?日志会告诉你。
好了,这一章的内容就到这里。下一章我们会深入Bootloader的配置和调试,到时候再聊。