1. 课程导入

在嵌入式Linux、服务器与桌面系统中,内核需要管理成千上万个设备:从SoC内部的总线控制器、GPIO、时钟、中断控制器,到外部的USB设备、PCIe网卡、I2C传感器、SPI闪存。若没有统一的抽象与组织方式,驱动开发者将陷入“每个驱动各自为政”的混乱局面。Linux设备模型(Linux Device Model, LDM)正是为解决这一问题而生的内核基础设施,它用总线(bus)、设备(device)、驱动(driver)、类(class)四个核心概念,把硬件拓扑、驱动匹配、电源管理、热插拔与用户空间可见性统一到一棵可遍历的树中。

一、Linux设备模型的核心原理

设备模型的本质是“用数据结构描述硬件关系”。内核启动时,总线驱动先注册总线类型(如platform、PCI、USB、I2C),随后设备通过设备树(Device Tree)、ACPI或板级文件被创建并挂到对应总线上;驱动则声明自己支持的设备ID表,由总线负责匹配。匹配成功后调用驱动的probe函数,完成资源申请、硬件初始化和字符/块/网络设备注册。

设备模型还维护三棵关键树:设备树(/sys/devices,反映物理拓扑)、驱动树(/sys/bus/*/drivers,反映驱动绑定)、类树(/sys/class,按功能分类,如net、tty、leds)。用户空间通过sysfs、uevent和devtmpfs感知设备变化,udev/mdev据此创建设备节点。

设备模型解决的核心问题:
1. 统一表示设备与驱动的匹配关系;
2. 自动管理设备生命周期(probe/remove/suspend/resume);
3. 支持热插拔与动态设备发现;
4. 为电源管理提供父子依赖顺序;
5. 向用户空间暴露稳定的设备视图。

二、设备注册与依赖管理为什么重要

设备注册不是简单的“把结构体塞进链表”。它涉及注册顺序、依赖解析、资源分配与失败回滚。例如,一个I2C温度传感器驱动在probe时必须先确保I2C适配器已注册、时钟已使能、GPIO已申请、 regulator已上电;若其中任一依赖未满足,probe会返回-EPROBE_DEFER,内核将该驱动放入延迟探测队列,待依赖就绪后重试。这种机制避免了驱动开发者手动编排初始化顺序,是Linux设备模型最精妙的设计之一。

依赖管理还体现在设备树的phandle引用、clocks、regulators、power-domains等属性上。内核通过device_link和fwnode建立设备间的有向依赖,确保suspend/resume按拓扑顺序执行,避免“子设备先于父设备断电”导致的挂死。

/* 典型probe中的依赖获取与延迟探测 */
static int my_sensor_probe(struct i2c_client *client)
{
    struct clk *clk;
    struct regulator *vdd;

    clk = devm_clk_get(&client->dev, "sensor_clk");
    if (IS_ERR(clk))
        return PTR_ERR(clk);          /* -EPROBE_DEFER 自动重试 */

    vdd = devm_regulator_get(&client->dev, "vdd");
    if (IS_ERR(vdd))
        return PTR_ERR(vdd);

    return devm_regulator_enable(vdd);
}

三、整体课程框架与学习路径

本课程共30章,围绕“设备如何被描述、注册、匹配、依赖、调试与优化”展开。前10章建立设备模型与总线/设备/驱动/类的基础认知;中10章深入设备树、platform、I2C/SPI/USB等具体总线注册流程与依赖解析;后10章聚焦probe失败排查、sysfs/debugfs调试、电源管理依赖、热插拔与工程化实践。

阶段章节范围核心目标关键产出
基础认知第1–10章理解bus/device/driver/class与sysfs能读懂/sys/devices拓扑
注册与依赖第11–20章掌握设备树、platform、I2C/SPI注册能编写可延迟探测的驱动
调试与工程化第21–30章probe失败分析、电源依赖、热插拔能定位并修复依赖顺序问题

四、典型场景与常见误区

场景:在一块ARM开发板上,I2C温湿度传感器驱动加载后/dev/i2c-1存在,但传感器节点未创建。排查发现驱动probe返回-EPROBE_DEFER,原因是传感器节点引用的vdd-supply对应的regulator驱动尚未注册。将regulator驱动改为built-in或调整设备树节点顺序后,传感器正常probe。

常见误区:
1. 认为“设备注册=驱动加载”,忽略总线匹配与probe时机;
2. 在probe中直接返回-ENODEV而非-EPROBE_DEFER,导致依赖就绪后不再重试;
3. 手动调用device_register()却未设置parent,破坏电源管理顺序;
4. 忽略devm_*资源管理,remove时资源泄漏。

下面的SVG以结构关系图的形式,展示Linux设备模型的核心对象、sysfs视图以及设备注册与依赖解析的主路径,帮助你在进入后续章节前建立整体心智模型。

Linux设备模型:核心对象与依赖解析路径 总线层 bus_type(platform / PCI / USB / I2C / SPI) match() 匹配 probe() 探测 remove() 移除 uevent 事件通知 设备 device • struct device / device_node • 资源:reg / irq / clocks / gpio • parent 指针构成设备树 • 注册:device_register() 驱动 driver • struct device_driver • id_table / of_match_table • probe / remove / suspend • 注册:driver_register() 匹配 类 class(功能视图) • /sys/class/net、tty、leds、input • 与设备多对多关联,便于用户空间查找 • class_create() / device_create() sysfs 用户空间视图 • /sys/devices 物理拓扑树 • /sys/bus/*/drivers 驱动绑定 • /sys/class 功能分类 依赖解析路径: 设备树 phandle → 资源获取 → 依赖未就绪返回 -EPROBE_DEFER → 延迟探测队列 → 依赖注册后重试 probe 关键:device_link 建立有向依赖,保证 suspend/resume 与电源域顺序正确