3. 设备生命周期模型
Linux 设备模型并不是“设备一插上就能用”这么简单。一个设备从出现在系统中,到真正被驱动接管、参与电源管理,再到被移除或关闭,要经历一条清晰的生命周期链路。理解这条链路,是理解设备电源协同的前提:电源状态并不是独立于设备生命周期的开关,而是随生命周期阶段动态迁移的伴生状态。
本节围绕设备注册、探测、绑定、解绑、移除、关闭六个关键阶段,说明每个阶段内核做了什么、电源状态处于什么位置,以及驱动开发者最容易在哪些环节踩坑。
一、核心原理:生命周期阶段与电源状态的对应关系
Linux 设备模型用 struct device 表示设备,用 struct device_driver 表示驱动,用总线(bus)负责匹配。设备生命周期大致分为“出现—匹配—激活—使用—停用—消失”六个阶段,而电源管理状态(如 runtime_suspended、runtime_active、system_suspend)则在这些阶段中迁移。
关键点在于:设备注册时通常处于“电源未激活”状态,探测成功后才可能进入运行时活跃;绑定建立后,电源管理回调才真正可用;解绑和移除阶段必须先把设备恢复到可安全关闭的状态。如果顺序错乱,就会出现“设备已移除但电源域仍开着”“驱动已解绑但时钟未关”等问题。
二、方法流程:各阶段的关键动作与电源关联
下面按阶段拆解内核实际执行的动作,以及电源管理介入的时机。
- 注册(device_register):设备被加入设备模型,分配
struct device,注册到总线。此时设备尚未与驱动匹配,电源域通常处于关闭或未知状态。驱动不应在此阶段访问硬件寄存器。 - 探测(probe):总线匹配到驱动后调用
probe()。这是驱动初始化硬件、申请资源(时钟、 regulator、GPIO、中断)的关键阶段。探测成功后,设备进入“已探测”状态,但运行时电源管理默认处于suspended,需要显式pm_runtime_enable()和pm_runtime_get_sync()才会进入活跃。 - 绑定(bind):驱动与设备建立正式关联,
dev->driver指向驱动,sysfs 中出现driver符号链接。此时电源管理回调(runtime_suspend、runtime_resume、system_suspend等)才真正可用。 - 运行/使用:设备处于
runtime_active,可正常收发数据、响应中断。电源管理根据使用计数动态切换。 - 解绑(unbind):驱动与设备解除关联,
remove()被调用。驱动必须在此阶段释放资源、关闭时钟、关闭 regulator,并调用pm_runtime_disable()。 - 移除(device_unregister):设备从设备模型移除,sysfs 节点消失。此时电源域应已关闭,否则会出现“幽灵供电”。
- 关闭(power_off):最终电源域断电,设备彻底停止工作。
核心原则:电源状态的迁移必须与生命周期阶段严格对齐。注册时不开电,探测时按需开电,解绑时先关电再释放资源,移除时确保电源已关闭。
三、案例场景:一个 I2C 温度传感器的生命周期
以 I2C 温度传感器为例,观察各阶段的实际日志与电源状态变化。
# 设备注册(设备树匹配后)
[ 2.345] i2c i2c-1: new_device: instantiated device tmp102 at 0x48
# 探测阶段
[ 2.350] tmp102 1-0048: probe: requesting regulator vdd
[ 2.355] tmp102 1-0048: probe: enabling runtime PM
[ 2.360] tmp102 1-0048: probe: success
# 绑定后 sysfs 可见
$ ls /sys/bus/i2c/devices/1-0048/
driver power subsystem ...
# 运行时活跃
$ cat /sys/bus/i2c/devices/1-0048/power/runtime_status
active
# 解绑(手动触发)
$ echo 1-0048 > /sys/bus/i2c/drivers/tmp102/unbind
[ 15.678] tmp102 1-0048: remove: disabling regulator vdd
[ 15.680] tmp102 1-0048: remove: runtime PM disabled
# 移除后电源状态
$ cat /sys/bus/i2c/devices/1-0048/power/runtime_status
(节点已消失)
这个案例说明:探测阶段申请 regulator 并启用 runtime PM,解绑阶段关闭 regulator 并禁用 runtime PM,移除后 sysfs 节点消失,电源域彻底关闭。如果解绑时忘记关闭 regulator,就会出现设备已移除但电源仍开启的异常。
四、误区与复盘:常见错误与排查方法
| 误区 | 现象 | 正确做法 |
|---|---|---|
| 在 probe 中直接访问硬件寄存器 | 系统挂起或崩溃 | 先 pm_runtime_get_sync() 再访问 |
忘记 pm_runtime_enable() |
runtime PM 不生效,设备一直活跃 | probe 中显式启用,remove 中禁用 |
| 解绑时未关闭 regulator | 设备移除后电源仍开启 | remove 中按申请逆序释放资源 |
| 移除后仍持有设备引用 | use-after-free 或电源域无法关闭 | 确保 put_device() 正确调用 |
忽略 system_suspend 回调 |
系统休眠时设备未进入低功耗 | 实现 dev_pm_ops 中的 suspend/resume |
排查技巧:使用 cat /sys/kernel/debug/pm_genpd/pm_genpd_summary 查看电源域状态;使用 dmesg | grep -i "probe\|remove\|runtime" 追踪生命周期日志;使用 ls /sys/bus/<bus>/devices/<dev>/power/ 查看运行时电源状态。
注意:设备生命周期阶段与电源状态的关联不是“自动”的,而是由驱动开发者显式维护的。内核只提供框架和回调,顺序和时机由驱动负责。任何一步遗漏,都可能导致电源泄漏、设备无法唤醒或系统休眠失败。
总结来说,设备生命周期模型是一条从注册到关闭的单向链路,电源状态是这条链路上的伴生状态。理解每个阶段内核做了什么、电源处于什么位置,才能写出健壮的驱动,避免“设备已移除但电源未关”这类典型问题。