Linux设备模型深度解析

发布于:2025-08-18 ⋅ 阅读:(15) ⋅ 点赞:(0)

深入分析 Linux 设备模型

一、工作原理

Linux 设备模型 (LDM) 的核心是 统一设备管理框架,通过以下机制实现:

  1. 抽象层次结构
    • 总线 (Bus) → 设备 (Device) → 驱动 (Driver) → 类 (Class)
    • 使用 kobject 实现面向对象模型(继承、多态)
  2. 自动匹配机制
    • 总线负责检测设备并与驱动匹配(match 函数)
  3. 统一用户接口
    • 通过 sysfs (/sys) 暴露设备信息
  4. 生命周期管理
    • 引用计数 (kref) 自动管理对象生存期
  5. 热插拔支持
    • 通过 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. 工作流程

  1. 总线注册bus_register() 创建 /sys/bus/xxx
  2. 设备注册device_register() → 添加到总线设备链表 → 触发匹配
  3. 驱动注册driver_register() → 遍历总线设备链表尝试匹配
  4. 匹配执行:调用总线的 match() 函数
  5. 绑定驱动:匹配成功则调用驱动的 probe()
  6. 移除路径:设备/驱动卸载时调用 remove() → 解绑

三、Linux设备模型核心目标

  1. 统一设备表示:抽象硬件设备为内核对象
  2. 动态电源管理:支持设备热插拔与电源状态转换
  3. 用户空间接口:通过sysfs暴露设备拓扑与属性
  4. 设备驱动绑定:自动匹配设备与驱动

四、核心架构与组件关系

KObject
SysFS接口
引用计数
KSet集合
KType操作集
Device设备
Driver驱动
Bus总线

五、核心数据结构与关系

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. 对象关系图
kobject
+char *name
+struct kref kref
+struct kset *kset
+struct kobj_type *ktype
device
+struct device *parent
+struct bus_type *bus
+struct device_driver *driver
+struct kobject kobj
device_driver
+char *name
+struct bus_type *bus
+int(*probe)(struct device *)
bus_type
+char *name
+int(*match)(struct device *, struct device_driver *)

六、设备模型工作流程

1. 设备注册流程
设备 总线 驱动 device_register() 添加到总线设备链表 遍历驱动链表调用match() probe()设备 绑定设备与驱动 等待后续驱动注册 alt [匹配成功] [匹配失败] 设备 总线 驱动
2. 驱动注册流程
驱动 总线 设备 driver_register() 添加到总线驱动链表 遍历设备链表调用match() 执行probe() 绑定设备与驱动 alt [匹配成功] 驱动 总线 设备

七、最小化代码实例(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/devicesdrivers 目录是否存在设备/驱动
  • 确认 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 集成到内核构建系统。


网站公告

今日签到

点亮在社区的每一天
去签到