1. 课程导学与环境准备:Linux驱动开发全景图
大家好,我是你们这门课的讲师。做了十几年嵌入式Linux,从裸机驱动一路折腾到复杂SoC,踩过的坑比代码还多。今天咱们先不急着敲键盘,把全景图铺开,把环境搭好。这一步走稳了,后面才跑得快。
1.1 Linux驱动开发全景图
先问大家一个问题:你写一个驱动,到底在写什么?
说白了,驱动就是操作系统和硬件之间的翻译官。上层应用调用open/read/write,驱动负责把这些操作翻译成硬件寄存器读写、中断处理、DMA搬运。整个过程分成三层:
- 应用层:用户空间程序,通过系统调用访问设备
- 内核层:驱动模块,运行在内核空间,处理硬件交互
- 硬件层:物理设备,寄存器、中断控制器、总线接口
我刚开始学驱动时,总觉得内核层很神秘。后来发现,其实内核就是给你搭好了一套框架——字符设备框架、平台设备框架、设备树机制。你只需要填自己的硬件逻辑就行。
核心认知:驱动开发 = 理解硬件手册 + 套用内核框架 + 调试到跑通。三者缺一不可。
1.2 课程目标与学习路径
这门课不是讲理论,是讲怎么把驱动跑起来。30章内容,我按实战路线设计的:
| 阶段 | 章节 | 核心产出 |
|---|---|---|
| 基础准备 | 1-3章 | 环境搭建、工具链配置、第一个驱动模块 |
| 驱动移植 | 4-12章 | 字符设备、平台驱动、设备树、中断、DMA |
| 存储与分区 | 13-18章 | Flash分区、UBIFS、块设备驱动 |
| 调试优化 | 19-25章 | printk/ftrace/perf、内存泄漏、性能调优 |
| 综合实战 | 26-30章 | 完整项目:摄像头驱动移植与优化 |
我的建议:别跳着学。每一章的实验环境都是基于前一章搭建的。我见过太多人直接跳到设备树那章,结果连模块加载都报错,回头补基础更浪费时间。
1.3 开发板选型建议
选开发板这事,我踩过不少坑。早期用某款国产板子,资料全是英文手册的机翻版,一个GPIO配置折腾了两天。后来换了主流板子,社区活跃,遇到问题一搜就有答案。
我个人推荐以下三款,覆盖不同预算和需求:
| 开发板 | SoC | 价格区间 | 适合场景 |
|---|---|---|---|
| Raspberry Pi 4B | BCM2711 (Cortex-A72) | ¥300-400 | 入门、社区资源丰富 |
| NXP i.MX8M Mini EVK | i.MX8M Mini (Cortex-A53) | ¥800-1200 | 工业级、Yocto/BSP完整 |
| TI AM335x BeagleBone Black | AM3358 (Cortex-A8) | ¥400-600 | 工控、RT-Linux、PRU |
避坑指南:我曾经图便宜买了一块某宝上的“全志H3核心板”,结果厂家提供的BSP是3.4内核,设备树都不支持。后来自己移植主线内核,花了整整一周。选板子,一定要确认厂家是否提供主线内核支持,或者社区是否有活跃维护。
1.4 虚拟机与Ubuntu安装
开发环境我建议用虚拟机,方便快照和回滚。Windows下用VMware Workstation,Linux下用VirtualBox都行。我个人习惯用VMware,稳定,快照功能好用。
Ubuntu版本选20.04 LTS或22.04 LTS。别追新,LTS版本稳定,软件源兼容性好。
安装步骤很简单,但有几个关键点要注意:
- 磁盘分配:至少给80GB,驱动编译、内核源码、交叉工具链都很占空间。我见过有人只给20GB,编译到一半磁盘满了。
- 内存分配:4GB起步,8GB更舒服。编译内核时内存不够会卡死。
- CPU核心:分配2-4核,make -jN时能加速。
- 网络模式:用桥接模式,方便开发板和虚拟机互通。
关键配置:安装完成后,执行以下命令更新软件源并安装基础开发工具:
sudo apt update
sudo apt upgrade -y
sudo apt install build-essential git vim curl wget \
libncurses-dev flex bison openssl libssl-dev \
dkms libelf-dev libudev-dev libpci-dev libiberty-dev \
autoconf automake device-tree-compiler -y
1.5 必备工具链安装
交叉编译链是驱动移植的核心工具。你想想看,在x86的PC上编译ARM架构的内核和驱动,没有交叉编译链根本做不到。
我推荐两种方式安装:
| 方式 | 命令/步骤 | 适用场景 |
|---|---|---|
| apt直接安装 | sudo apt install gcc-arm-linux-gnueabihf |
快速开始,版本较旧但稳定 |
| Linaro官方工具链 | 下载gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz | 版本新,支持更多特性 |
我个人习惯用Linaro的,因为版本可控,项目里统一用7.5.0,不会因为apt源更新导致编译行为变化。
安装Linaro工具链的步骤:
# 下载
wget https://releases.linaro.org/components/toolchain/binaries/7.5-2019.12/arm-linux-gnueabihf/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz
# 解压到 /opt
sudo tar -xvf gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz -C /opt
# 添加环境变量
echo 'export PATH=$PATH:/opt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin' >> ~/.bashrc
source ~/.bashrc
# 验证
arm-linux-gnueabihf-gcc --version
验证小技巧:执行 arm-linux-gnueabihf-gcc -v 看到版本信息后,再编译一个hello.c试试:arm-linux-gnueabihf-gcc hello.c -o hello.arm,然后用 file hello.arm 查看,输出应该是 ELF 32-bit LSB executable, ARM。这样才算真正装好了。
1.6 环境验证:第一个驱动模块
环境搭好了,咱们写个最简单的驱动模块验证一下。这个模块什么都不做,只打印一条日志。
// hello_drv.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
static int __init hello_init(void)
{
printk(KERN_INFO "Hello, Linux Driver World!\n");
return 0;
}
static void __exit hello_exit(void)
{
printk(KERN_INFO "Goodbye, Linux Driver World!\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple Hello World driver");
编译这个模块需要内核源码树。下载内核源码并配置:
# 下载内核源码(以5.10为例)
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.100.tar.xz
tar -xvf linux-5.10.100.tar.xz
cd linux-5.10.100
# 配置内核(使用默认配置)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- defconfig
# 编译内核(第一次编译较慢,-jN根据CPU核心数调整)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4
然后编译驱动模块:
# 在hello_drv.c所在目录创建Makefile
obj-m += hello_drv.o
all:
make -C /path/to/linux-5.10.100 M=$(PWD) ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules
clean:
make -C /path/to/linux-5.10.100 M=$(PWD) ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- clean
常见错误:如果编译时报错 Kernel configuration is invalid,说明内核没编译过。先执行 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- defconfig 再编译。我曾经在这上面浪费了半小时,后来发现是忘了先配置内核。
编译成功后,会生成 hello_drv.ko。把这个文件传到开发板上,执行:
insmod hello_drv.ko
dmesg | tail -5
# 应该看到:Hello, Linux Driver World!
看到这条日志,说明你的环境全部就绪了。嗯,到这里,第一章的准备工作就完成了。
本章核心收获:
- 理解了Linux驱动开发的三层架构
- 选好了开发板并搭建了虚拟机环境
- 安装了交叉编译链并验证了可用性
- 成功编译并加载了第一个驱动模块