1. 嵌入式Linux驱动开发概述
1.1 嵌入式系统简介
嵌入式系统是以应用为中心、以计算机技术为基础,软硬件可裁剪,适用于对功能、可靠性、成本、体积、功耗有严格要求的专用计算机系统。与通用PC不同,嵌入式系统通常运行在资源受限的硬件平台上,执行特定任务。
核心特征: 专用性、资源受限、高可靠性、实时性要求、低功耗设计。
嵌入式系统的典型组成
| 层次 | 组成要素 | 说明 |
|---|---|---|
| 硬件层 | CPU/SoC、内存、外设控制器 | ARM Cortex-A/M/R、RISC-V、x86嵌入式 |
| 固件层 | Bootloader、固件驱动 | U-Boot、Coreboot、ATF |
| 内核层 | Linux内核、设备驱动 | 进程调度、内存管理、文件系统、网络栈 |
| 用户层 | 根文件系统、应用软件 | BusyBox、Qt、OpenWrt、Yocto |
嵌入式系统分类(按性能与实时性)
- 深度嵌入式(MCU类): Cortex-M、RISC-V MCU,裸机或RTOS,资源极有限。
- 实时嵌入式: 带硬件实时单元,运行RT-Linux或Xenomai,用于工业控制、汽车。
- 通用嵌入式(MPU类): Cortex-A系列,运行完整Linux,用于智能终端、网关、工控板。
- 高端嵌入式: 多核SoC + GPU/NPU,运行Linux + 容器,用于边缘计算、自动驾驶。
1.2 Linux内核架构概览
Linux内核采用宏内核(Monolithic Kernel)架构,但通过模块化设计支持动态加载和卸载驱动。内核运行在特权级(EL1/内核态),为用户空间提供系统调用接口。
内核核心子系统
| 子系统 | 职责 | 关键文件/目录 |
|---|---|---|
| 进程调度(Scheduler) | 进程/线程创建、调度策略(CFS、RT) | kernel/sched/ |
| 内存管理(MM) | 虚拟内存、页分配、SLAB、DMA | mm/ |
| 虚拟文件系统(VFS) | 统一文件抽象,支持ext4、proc、sysfs | fs/ |
| 网络栈(Networking) | 协议栈、套接字、Netfilter | net/ |
| 设备驱动模型(Driver Model) | 设备枚举、总线、驱动绑定、udev | drivers/base/ |
| 中断管理(IRQ) | 中断控制器、中断线程化、IRQ域 | kernel/irq/ |
驱动在内核中的位置
设备驱动是内核中代码量最大的部分(约占70%以上),它们作为内核模块或内置代码,负责控制具体的硬件外设。驱动通过文件操作接口(struct file_operations)向用户空间暴露设备节点(/dev/xxx),或通过子系统框架(如I2C、SPI、ALSA、V4L2)与内核核心交互。
1.3 驱动开发环境搭建
嵌入式Linux驱动开发采用交叉编译模式:在性能强大的宿主机(x86_64 PC)上编写代码、编译,生成目标平台(ARM / RISC-V / MIPS)的可执行文件或内核模块,再部署到目标板运行。
1.3.1 交叉编译工具链
交叉编译工具链由编译器(gcc)、汇编器(as)、链接器(ld)、库(glibc/uClibc/musl)和调试器(gdb)组成,命名格式通常为:
# 工具链前缀示例(ARM Cortex-A9)
arm-linux-gnueabihf-gcc
arm-linux-gnueabihf-ld
arm-linux-gnueabihf-objdump
# 工具链前缀示例(ARM64 / AArch64)
aarch64-linux-gnu-gcc
aarch64-linux-gnu-objcopy
# 工具链前缀示例(RISC-V 64)
riscv64-linux-gnu-gcc
常用工具链获取方式
| 方式 | 来源 | 适用场景 |
|---|---|---|
| Linaro GCC | releases.linaro.org | ARM 32/64位通用开发 |
| Buildroot | buildroot.org | 自定义工具链 + 根文件系统 |
| Yocto/OpenEmbedded | yoctoproject.org | 企业级嵌入式Linux发行版 |
| SoC厂商SDK | NXP / TI / Rockchip / Allwinner | 特定SoC平台开发 |
| 交叉编译器预编译包 | apt / yum 仓库 | 快速安装(如 gcc-arm-linux-gnueabihf) |
建议: 初学者推荐使用Linaro预编译工具链或Buildroot一键生成,避免手动编译工具链的复杂依赖。
1.3.2 内核源码树
驱动开发需要完整的内核源码树,用于编译内核模块和生成头文件依赖。内核源码树包含以下关键目录:
linux-6.x/
├── arch/ # 体系结构相关代码(arm64, arm, riscv, x86 ...)
├── block/ # 块层I/O调度
├── crypto/ # 加密API
├── Documentation/ # 内核文档
├── drivers/ # 设备驱动(最大目录)
│ ├── char/ # 字符设备
│ ├── net/ # 网络设备
│ ├── i2c/ # I2C总线驱动
│ ├── spi/ # SPI总线驱动
│ ├── gpio/ # GPIO驱动
│ ├── mmc/ # MMC/SD卡驱动
│ ├── usb/ # USB驱动
│ └── video/ # 视频/显示驱动
├── fs/ # 文件系统实现
├── include/ # 内核头文件
├── init/ # 内核初始化
├── kernel/ # 核心子系统(调度、irq、time)
├── lib/ # 内核工具函数
├── mm/ # 内存管理
├── net/ # 网络协议栈
├── scripts/ # 编译脚本、Kconfig、DT工具
├── sound/ # 音频子系统(ALSA)
├── tools/ # 用户空间工具
├── Makefile # 顶层Makefile
├── Kconfig # 顶层配置菜单
└── arch/arm64/configs/defconfig # 默认内核配置
1.3.3 环境搭建步骤
- 安装交叉编译工具链
# Ubuntu/Debian 安装 ARM 工具链 sudo apt update sudo apt install gcc-arm-linux-gnueabihf binutils-arm-linux-gnueabihf # 验证安装 arm-linux-gnueabihf-gcc --version - 获取内核源码并配置
# 从 kernel.org 下载稳定版本 wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.6.tar.xz tar -xf linux-6.6.tar.xz cd linux-6.6 # 使用目标板默认配置(以ARM64为例) make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- defconfig # 或使用menuconfig进行定制 make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- menuconfig - 编译内核(可选,驱动开发至少需编译模块依赖)
# 编译内核镜像 make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc) # 编译内核模块 make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- modules # 生成模块依赖(用于后续模块编译) make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- modules_prepare - 编写第一个驱动模块(Hello World)
// hello.c #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> static int __init hello_init(void) { printk(KERN_INFO "Hello, Embedded Linux Driver!\n"); return 0; } static void __exit hello_exit(void) { printk(KERN_INFO "Goodbye, Embedded Linux Driver!\n"); } module_init(hello_init); module_exit(hello_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("A simple Hello World driver"); - 编写Makefile并编译模块
# Makefile obj-m := hello.o KERNELDIR ?= /path/to/linux-6.6 CROSS_COMPILE ?= aarch64-linux-gnu- CC := $(CROSS_COMPILE)gcc all: make -C $(KERNELDIR) M=$(PWD) ARCH=arm64 CROSS_COMPILE=$(CROSS_COMPILE) modules clean: make -C $(KERNELDIR) M=$(PWD) ARCH=arm64 CROSS_COMPILE=$(CROSS_COMPILE) clean - 部署到目标板并测试
# 将 hello.ko 拷贝到目标板 scp hello.ko root@192.168.1.100:/root/ # 在目标板加载模块 insmod hello.ko # 查看内核日志 dmesg | tail # 卸载模块 rmmod hello.ko
常见问题: 内核版本必须与目标板运行的内核版本一致,否则模块加载会报“Invalid module format”或“version magic mismatch”。使用
modinfo hello.ko 可查看模块的 vermagic 信息。
1.3.4 开发环境验证清单
| 检查项 | 验证命令 | 预期结果 |
|---|---|---|
| 工具链可用 | arm-linux-gnueabihf-gcc --version |
显示gcc版本信息 |
| 内核源码存在 | ls linux-6.6/Makefile |
文件存在 |
| 内核已配置 | ls linux-6.6/.config |
文件存在 |
| 模块编译环境 | ls linux-6.6/Module.symvers |
文件存在(modules_prepare后生成) |
| 目标板连通 | ping 192.168.1.100 |
网络可达 |
1.3.5 推荐开发环境组合
- 入门组合: Ubuntu 22.04 LTS + Linaro GCC 7.5 + Linux 6.6 LTS + QEMU模拟ARM64
- 进阶组合: Buildroot 2024.02 + 自定义工具链 + 真实开发板(Raspberry Pi 4 / STM32MP157 / i.MX8M)
- 企业组合: Yocto Kirkstone + 厂商BSP + JTAG调试器 + Trace32
本章小结: 嵌入式Linux驱动开发需要理解嵌入式系统的分层特性、Linux内核的模块化架构,并搭建好交叉编译环境。掌握工具链配置、内核源码树结构以及模块编译流程,是后续深入学习字符设备驱动、总线驱动、中断管理等内容的基础。