1. 课程导入

在嵌入式Linux与驱动开发领域,很多初学者会陷入一种困境:能写字符设备驱动,能调用register_chrdev,但一旦面对/sys目录下层层嵌套的目录结构、面对设备与驱动自动匹配的机制、面对电源管理和热插拔场景,就感到无从下手。造成这种困境的根本原因,是缺少对Linux设备模型(Linux Device Model)与总线框架的整体认知。本课程的目标,就是帮助学习者从"能写驱动"进阶到"理解内核如何组织设备与驱动",并最终具备独立开发总线型驱动、平台驱动与设备树驱动框架的能力。

一、学习目标:从驱动编写者到框架设计者

本课程的学习目标可以分为三个层次。第一层是理解核心抽象:掌握kobject、kset、device、driver、bus、class这六个核心对象之间的引用与归属关系,理解它们如何共同构建出/sys下的目录树。第二层是掌握匹配机制:理解总线如何通过match回调完成设备与驱动的绑定,理解probe与remove的调用时机。第三层是具备框架开发能力:能够基于platform总线、I2C总线或自定义总线,编写符合设备模型的完整驱动,并能通过sysfs向用户空间暴露可控属性。

学习本课程后,你应当能够回答:为什么插入一个USB设备,内核能自动找到对应驱动?为什么/sys/bus/platform/devices下会出现设备节点?device_register内部究竟做了什么?这些问题的答案,构成了Linux设备模型的知识骨架。

二、核心概念:设备模型的对象关系

Linux设备模型本质上是一套用C语言实现的面向对象框架。内核用kobject表示一个可被sysfs引用的基础对象,它负责引用计数与生命周期管理;kset是kobject的集合,用于组织层级;device表示一个物理或逻辑设备;device_driver表示能够驱动某类设备的驱动;bus_type则是设备与驱动相遇的"中介",负责匹配与绑定。它们之间的关系可以用下面的结构图清晰表达。

Linux 设备模型核心对象关系图 kobject 引用计数 / sysfs入口 kset kobject 集合 / 层级组织 bus_type match / probe / remove device 设备实例 / 资源描述 device_driver 驱动逻辑 / probe实现 class 用户空间接口分类 sysfs /sys 虚拟文件系统 包含 组织 匹配 绑定 归属 导出 聚合

理解这张图的关键在于:bus_type是设备与驱动之间的桥梁,device和device_driver分别挂载到总线的两个链表上,总线通过match函数判断二者是否匹配,匹配成功后调用驱动的probe函数完成初始化。而kobject与kset则贯穿始终,负责将这一切映射到/sys文件系统中,供用户空间观察和操作。

三、整体知识框架与开发环境搭建

本课程的知识框架按照"基础抽象 → 总线机制 → 典型总线实战 → 高级主题"的顺序展开。前四章聚焦设备模型基础,包括kobject、kset、sysfs与属性文件;中间章节深入总线匹配、platform总线、I2C与SPI总线框架;后续章节覆盖设备树、电源管理、热插拔与驱动调试。学习路径如下表所示。

阶段 核心内容 关键API/对象 实践目标
基础抽象 kobject、kset、sysfs kobject_init、kset_create 手动创建sysfs节点
设备与驱动 device、driver、bus_type device_register、driver_register 实现自定义总线匹配
典型总线 platform、I2C、SPI platform_driver_register 编写平台设备驱动
高级主题 设备树、电源管理、调试 of_match_table、dev_pm_ops 设备树驱动与功耗控制

开发环境建议使用Ubuntu 22.04 LTS作为宿主系统,搭配QEMU模拟的ARM64虚拟机或树莓派4B作为目标板。内核源码建议选择Linux 6.1 LTS版本,该版本设备模型稳定且文档完善。编译工具链使用aarch64-linux-gnu-交叉编译器。以下命令用于准备基础环境:

# 安装依赖工具
sudo apt install build-essential libncurses-dev bison flex \
     libssl-dev libelf-dev bc git

# 获取内核源码
git clone --depth=1 -b v6.1 https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git

# 配置并编译ARM64内核
cd linux
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- defconfig
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc)

# 查看设备模型相关目录
ls /sys/bus/
ls /sys/devices/
ls /sys/class/
建议在QEMU中启动内核后,通过ls -l /sys/bus/platform/devices/观察设备节点,再结合dmesg | grep -i probe查看驱动匹配日志。这种"观察—验证—修改"的循环,是掌握设备模型最有效的方式。

四、常见误区与学习复盘

初学者最容易陷入的误区有三个。其一,把device和driver混为一谈,认为注册了驱动设备就能工作,实际上二者必须通过总线匹配后才能触发probe。其二,忽视kobject的引用计数,导致模块卸载时出现use-after-free或内存泄漏。其三,直接操作/sys下的文件而不理解其背后的attribute机制,导致属性读写方向混乱。

特别注意:device_register与driver_register的调用顺序并不强制,但若设备先注册而驱动后注册,总线会在驱动注册时主动发起一次匹配扫描;反之亦然。理解这一点,才能解释为什么有时驱动加载后设备才出现,却依然能自动probe。