深入分析 Linux 设备模型
一、工作原理
Linux 设备模型 (LDM) 的核心是 统一设备管理框架,通过以下机制实现:
- 抽象层次结构:
- 总线 (Bus) → 设备 (Device) → 驱动 (Driver) → 类 (Class)
- 使用
kobject
实现面向对象模型(继承、多态)
- 自动匹配机制:
- 总线负责检测设备并与驱动匹配(
match
函数)
- 总线负责检测设备并与驱动匹配(
- 统一用户接口:
- 通过
sysfs
(/sys
) 暴露设备信息
- 通过
- 生命周期管理:
- 引用计数 (
kref
) 自动管理对象生存期
- 引用计数 (
- 热插拔支持:
- 通过
uevent
机制通知用户空间(如 udev)
- 通过
二、实现机制与代码框架
核心组件关系图:
kobject → kset → subsystem
↑
device → device_driver
↑ ↑
bus_type
1. 核心数据结构:
// <linux/kobject.h>
struct kobject {
const char *name; // sysfs 目录名
struct list_head entry; // kset 链表节点
struct kobject *parent; // 父对象(形成层次结构)
struct kset *kset; // 所属集合
struct kobj_type *ktype; // 对象类型(操作函数)
struct kernfs_node *sd; // sysfs 节点
struct kref kref; // 引用计数
};
// <linux/device.h>
struct device {
struct kobject kobj; // 继承 kobject
struct device *parent; // 父设备
struct device_driver *driver; // 绑定的驱动
struct bus_type *bus; // 所属总线
void *platform_data; // 设备私有数据
// ...
};
struct device_driver {
const char *name; // 驱动名称
struct bus_type *bus; // 所属总线
int (*probe)(struct device *dev); // 设备探测函数
int (*remove)(struct device *dev);// 设备移除函数
struct driver_private *p; // 私有数据
};
struct bus_type {
const char *name; // 总线名(如 "platform")
int (*match)(struct device *dev, struct device_driver *drv); // 匹配函数
struct kset devices; // 总线上所有设备
struct kset drivers; // 总线上所有驱动
};
2. 工作流程:
- 总线注册:
bus_register()
创建/sys/bus/xxx
- 设备注册:
device_register()
→ 添加到总线设备链表 → 触发匹配 - 驱动注册:
driver_register()
→ 遍历总线设备链表尝试匹配 - 匹配执行:调用总线的
match()
函数 - 绑定驱动:匹配成功则调用驱动的
probe()
- 移除路径:设备/驱动卸载时调用
remove()
→ 解绑
三、Linux设备模型核心目标
- 统一设备表示:抽象硬件设备为内核对象
- 动态电源管理:支持设备热插拔与电源状态转换
- 用户空间接口:通过
sysfs
暴露设备拓扑与属性 - 设备驱动绑定:自动匹配设备与驱动
四、核心架构与组件关系
五、核心数据结构与关系
1. 关键结构体
结构体 | 作用 | 关键成员 |
---|---|---|
struct kobject |
基础对象 | name , kref , parent , kset , ktype |
struct kset |
kobject容器 | list (对象链表), kobj (内嵌kobject) |
struct ktype |
定义对象行为 | release (释放函数), sysfs_ops |
struct device |
设备抽象 | parent , bus , driver , kobj |
struct device_driver |
驱动抽象 | name , bus , probe , remove |
struct bus_type |
总线抽象 | name , match , probe , remove |
2. 对象关系图
六、设备模型工作流程
1. 设备注册流程
2. 驱动注册流程
七、最小化代码实例(Platform设备/驱动)
场景:实现一个虚拟平台设备(my_device
)和驱动(my_driver
)
1. 设备端代码 (my_device.c
):
#include <linux/module.h>
#include <linux/platform_device.h>
static struct platform_device my_device = {
.name = "my_device", // 匹配驱动名称
.id = -1,
};
static int __init my_device_init(void) {
platform_device_register(&my_device);
printk("My device registered\n");
return 0;
}
static void __exit my_device_exit(void) {
platform_device_unregister(&my_device);
printk("My device unregistered\n");
}
module_init(my_device_init);
module_exit(my_device_exit);
MODULE_LICENSE("GPL");
2. 驱动端代码 (my_driver.c
):
#include <linux/module.h>
#include <linux/platform_device.h>
static int my_probe(struct platform_device *pdev) {
printk("Device probed! Driver attached.\n");
return 0;
}
static int my_remove(struct platform_device *pdev) {
printk("Device removed. Driver detached.\n");
return 0;
}
static struct platform_driver my_driver = {
.driver = {
.name = "my_device", // 匹配设备名称
.owner = THIS_MODULE,
},
.probe = my_probe,
.remove = my_remove,
};
module_platform_driver(my_driver); // 自动注册/注销驱动
MODULE_LICENSE("GPL");
3. 测试步骤:
# 编译加载模块
sudo insmod my_device.ko
sudo insmod my_driver.ko
# 查看日志
dmesg | tail
# 输出示例:
# [ 123.456] My device registered
# [ 124.789] Device probed! Driver attached
# 查看 sysfs 结构
ls /sys/bus/platform/devices/my_device
# 包含:driver, power, subsystem 等符号链接
# 卸载模块
sudo rmmod my_driver my_device
八、调试工具与命令
1. SysFS 关键路径
路径 | 作用 |
---|---|
/sys/bus/ |
所有总线类型 |
/sys/devices/ |
设备物理层次结构 |
/sys/class/ |
按功能分类的设备视图 |
/sys/kernel/debug/devices_deferred |
延迟探测的设备列表 |
2. 关键命令
# 查看设备树
ls -l /sys/devices/
# 查看总线绑定关系
cat /sys/bus/platform/devices/my_platdev/driver
# 动态打印内核消息
dmesg -w | grep "my_platdev"
# 触发设备探测(手动绑定)
echo my_platdev > /sys/bus/platform/drivers/my_platdev/bind
# 查看设备资源信息
cat /proc/iomem | grep my_platdev
3. DebugFS 调试
# 查看设备引用计数
cat /sys/kernel/debug/kobject/my_platdev/kref
# 跟踪设备事件
echo 1 > /sys/kernel/debug/tracing/events/device/enable
cat /sys/kernel/debug/tracing/trace_pipe
九、核心机制深度解析
1. kobject 生命周期管理
struct kobject *kobj;
kobject_init(kobj, &my_ktype); // 初始化引用计数为1
kobject_add(kobj, parent, "name"); // 添加到sysfs
kobject_put(kobj); // 减少引用计数,为0时调用release()
2. 设备-驱动匹配逻辑
总线match()
函数典型实现:
static int platform_match(struct device *dev, struct device_driver *drv)
{
return strcmp(dev_name(dev), drv->name) == 0;
}
3. SysFS 属性操作
static ssize_t status_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%s\n", get_status(dev));
}
static DEVICE_ATTR_RO(status); // 创建只读属性 /sys/devices/.../status
十、常见问题调试技巧
1. Sysfs 导航工具:
# 查看设备层次
tree /sys/devices/ | less
# 查看总线列表
ls /sys/bus/
# 查看设备绑定状态
cat /sys/bus/platform/devices/my_device/driver
# 输出应指向 /sys/bus/platform/drivers/my_device
2. Uevent 调试:
# 监控内核事件
udevadm monitor -k -p
# 触发事件(测试热插拔)
echo 1 > /sys/bus/platform/devices/my_device/uevent
3. 动态调试:
// 在驱动代码中添加
#include <linux/dynamic_debug.h>
#define dev_dbg(dev, fmt, ...) dynamic_dev_dbg(dev, fmt, ##__VA_ARGS__)
# 启用动态调试
echo "file my_driver.c +p" > /sys/kernel/debug/dynamic_debug/control
4. Ftrace 跟踪:
# 跟踪设备注册过程
echo function_graph > /sys/kernel/debug/tracing/current_tracer
echo "bus_register device_add driver_register" > set_ftrace_filter
echo 1 > /sys/kernel/debug/tracing/tracing_on
# 加载模块后查看 trace
cat trace
5. KDB/KGDB:
# 在内核崩溃时检查设备状态
kdb> lsdev # 列出所有设备
kdb> drvlist # 列出驱动
6. 匹配失败排查:
- 检查
/sys/bus/xxx/devices
和drivers
目录是否存在设备/驱动 - 确认
match()
函数返回值(需返回 1 表示匹配)
7. Probe 失败分析:
- 在
probe
函数添加-EPROBE_DEFER
实现依赖重试 - 使用
dev_err(&pdev->dev, "Error message")
输出详细错误
8. 内存泄漏检测:
# 检查 kobject 引用
grep my_device /sys/kernel/debug/kobject_leak
9. Sysfs 属性调试:
// 添加可读写属性
static ssize_t status_show(struct device *dev, struct device_attribute *attr, char *buf) {
return sprintf(buf, "OK\n");
}
static DEVICE_ATTR_RO(status); // 创建 /sys/devices/.../my_device/status
总结
Linux 设备模型通过四大核心机制(对象抽象、自动匹配、sysfs 接口、热插拔)实现统一设备管理。开发者需掌握:
- 总线/设备/驱动的注册流程
kobject
的生命周期管理probe/remove
的调用时机- Sysfs 调试工具链
实际开发中建议结合devres
API 管理资源,并善用动态调试提高效率。
最终输出:包含 2 个可编译模块的完整代码包(需替换
my_device
/my_driver
为实际名称),可通过 Makefile 集成到内核构建系统。