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 + 容器,用于边缘计算、自动驾驶。
嵌入式系统分层架构图 应用层(用户空间) 系统库 / C运行时库(glibc / musl) Linux内核(进程调度 / 内存管理 / 网络栈 / VFS) 设备驱动层(字符 / 块 / 网络 / 总线驱动) 硬件抽象层 / Bootloader / 固件

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)与内核核心交互。

Linux内核架构概览与驱动交互路径 用户空间(应用程序 / C库 / 系统调用接口) 系统调用层(SYSCALL_DEFINE / 系统调用表) 虚拟文件系统(VFS) 网络协议栈(TCP/IP / Socket) 设备驱动模型(总线 / 设备 / 驱动 / class) 字符设备驱动 块设备驱动 网络设备驱动 硬件层(SoC外设 / 总线控制器 / 物理设备)

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 环境搭建步骤

  1. 安装交叉编译工具链
    # Ubuntu/Debian 安装 ARM 工具链
    sudo apt update
    sudo apt install gcc-arm-linux-gnueabihf binutils-arm-linux-gnueabihf
    
    # 验证安装
    arm-linux-gnueabihf-gcc --version
  2. 获取内核源码并配置
    # 从 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
  3. 编译内核(可选,驱动开发至少需编译模块依赖)
    # 编译内核镜像
    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
  4. 编写第一个驱动模块(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");
  5. 编写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
  6. 部署到目标板并测试
    # 将 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 网络可达
交叉编译环境工作流程 宿主机(x86_64) 交叉工具链 + 内核源码树 编写驱动代码 → 交叉编译 传输部署 scp / NFS / TFTP / USB 目标板(ARM64) 运行Linux内核 insmod / rmmod 测试驱动 调试反馈(串口日志 / dmesg / gdb)

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内核的模块化架构,并搭建好交叉编译环境。掌握工具链配置、内核源码树结构以及模块编译流程,是后续深入学习字符设备驱动、总线驱动、中断管理等内容的基础。