第四章:嵌入式Linux内核编译——获取源码、配置与烧录
好,到了这一章,我们终于要跟内核源码打交道了。说实话,很多初学者觉得编译内核是个很玄乎的事情,其实说白了,就是三步:拿到源码、告诉内核你要什么、然后编译成能跑的东西。我在带团队的时候,经常跟新人说,内核编译就像做饭——你得先有菜(源码),然后决定口味(配置),最后开火(编译)。
4.1 获取内核源码
获取源码的方式有好几种,我个人的习惯是直接从kernel.org下载主线版本。但做嵌入式开发,你最好用芯片厂商提供的BSP内核。为什么?因为主线内核可能没有你板子的驱动支持。
常用的获取方式:
- 官网下载:从
https://www.kernel.org获取稳定版本 - 芯片厂商BSP:比如NXP、Rockchip、Allwinner提供的定制内核
- Git仓库克隆:
git clone git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
我的经验:如果你用的是开发板,比如树莓派或BeagleBone,直接去官方GitHub拉取他们的内核分支。我踩过坑——用主线内核去编译BeagleBone的驱动,结果GPIO死活点不亮灯,折腾了两天才发现是设备树不匹配。
下载完成后,解压或者进入你的内核目录:
tar -xvf linux-5.10.100.tar.xz
cd linux-5.10.100
4.2 配置内核选项
配置内核,说白了就是告诉内核:你要支持哪些硬件、哪些文件系统、哪些网络协议。嗯,这里要注意,配置不对,编译出来的内核要么太大,要么缺驱动。
常用的配置命令:
make menuconfig—— 基于ncurses的图形界面,我最常用make defconfig—— 使用默认配置,适合快速验证make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig—— 交叉编译时指定架构
举个例子,你要添加摄像头驱动支持(V4L2),在menuconfig里这样操作:
- 进入
Device Drivers - 选择
Multimedia support - 勾选
Video for Linux和V4L2 sub-device userspace API - 在
Media drivers里选择你的摄像头芯片型号
关键点:配置时,Y 表示编译进内核,M 表示编译成模块(.ko文件),N 表示不编译。我建议把不常用的驱动编成模块,这样内核体积小,启动快。
我曾经犯过一个低级错误:把USB摄像头驱动编成了模块,但文件系统里没有加载模块的脚本,结果摄像头插上去完全没反应。后来我改成编译进内核,问题就解决了。
4.3 编译内核
配置完成后,就是编译了。交叉编译的命令格式:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4 zImage
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4 modules
这里解释一下:
ARCH=arm指定目标架构CROSS_COMPILE=arm-linux-gnueabihf-指定交叉编译工具链前缀-j4表示用4个线程并行编译,加快速度zImage是压缩后的内核镜像modules编译所有标记为M的驱动模块
编译完成后,你会得到这些文件:
| 文件 | 说明 |
|---|---|
| arch/arm/boot/zImage | 内核镜像 |
| arch/arm/boot/dts/你的板子.dtb | 设备树文件 |
| drivers/.../*.ko | 内核模块 |
注意:编译时间取决于你的机器性能。我有一台老笔记本,编译一次要40分钟。后来我换了台i7的机器,8分钟搞定。如果你用虚拟机,记得多分点CPU和内存。
4.4 烧录到开发板
编译好了,怎么把内核放到板子上?常见的方式有几种:
- TFTP网络加载:适合调试阶段,不用反复烧写
- SD卡烧录:把内核放到SD卡分区,U-Boot读取
- NAND/NOR Flash烧写:量产时用
我以SD卡方式为例,假设你的开发板是ARM架构:
# 挂载SD卡第一个分区(通常是FAT格式,存放内核和设备树)
sudo mount /dev/sdb1 /mnt/boot
# 复制内核和设备树
sudo cp arch/arm/boot/zImage /mnt/boot/
sudo cp arch/arm/boot/dts/你的板子.dtb /mnt/boot/
# 挂载第二个分区(根文件系统)
sudo mount /dev/sdb2 /mnt/rootfs
# 安装内核模块
sudo make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules_install INSTALL_MOD_PATH=/mnt/rootfs
# 卸载
sudo umount /mnt/boot /mnt/rootfs
然后插回开发板,上电启动。如果一切正常,你会看到内核启动日志,最后出现登录提示符。
调试技巧:如果启动卡住了,可以在U-Boot里设置 bootargs 加上 earlyprintk 参数,这样内核在早期初始化阶段就会输出调试信息。我曾经靠这个参数找到了一个时钟配置错误的问题。
4.5 常见问题与避坑
编译内核时,你可能会遇到这些问题:
- 缺少ncurses库:运行menuconfig时报错,安装
libncurses-dev即可 - 交叉编译工具链找不到:检查环境变量
PATH是否包含工具链路径 - 设备树编译错误:检查dts文件语法,可以用
make dtbs单独编译 - 内核启动时panic:多半是根文件系统挂载失败,检查
root=参数
我记得有一次,编译出来的内核有3MB,但我的板子Flash只有2MB空间。后来我通过 make menuconfig 去掉了很多不需要的驱动和调试选项,内核缩小到1.2MB,顺利烧录。
好了,这一章的内容就到这里。你只要跟着步骤走,把内核编译出来并烧到板子上,摄像头驱动开发的环境就算搭好了。下一章我们会开始写第一个字符设备驱动,敬请期待。