当前位置: 代码网 > 服务器>服务器>Linux > 一篇文章搞懂Linux设备模型:kobject、sysfs和总线驱动匹配机制

一篇文章搞懂Linux设备模型:kobject、sysfs和总线驱动匹配机制

2026年07月16日 Linux 我要评论
基于内核版本:linux 2.6+(核心框架),linux 3.10+(现代范式),linux 6.x(当前主线)一、概述与核心设计哲学linux 的设备体系不是简单的驱动集合,而是一套面向对象的内核

基于内核版本:linux 2.6+(核心框架),linux 3.10+(现代范式),linux 6.x(当前主线)

一、概述与核心设计哲学

linux 的设备体系不是简单的驱动集合,而是一套面向对象的内核对象管理体系,其设计哲学可概括为三句话:

哲学含义技术体现
一切皆对象所有设备、驱动、总线、类都是内核对象struct kobject 为原子基类
总线即纽带总线负责撮合设备与驱动bus_type.match() → driver.probe()
一切皆文件用户空间通过文件接口访问设备/dev 节点 + /sys 属性文件

整个体系自底向上分为四层:

上图:linux 设备模型与驱动框架四层架构。自上而下依次为 用户空间 → 设备管理层 → 设备模型层 → 硬件抽象层,箭头表示上层对下层的依赖关系。

二、linux 设备模型(ldm)

定型版本:linux 2.6(2003 年) 

当前状态:核心结构体自 2.6 以来未发生破坏性变更,沿用至 6.x 主线。

2.1 核心对象链

linux 设备模型的原子单元是 kobject,所有设备、驱动、总线、类都直接或间接继承它。

结构体作用关键成员首次引入
struct kobject内核对象基类name, kref, parent, kset, ktypelinux 2.5
struct ksetkobject 的容器list(对象链表),内嵌 kobjlinux 2.5
struct ktype定义对象行为release(), sysfs_opslinux 2.5
struct subsys_private总线/子系统私有数据管理 devices/drivers 链表linux 2.6.15

对象层次关系:

kobject (单个对象,如一个 led 设备)
    ↓ 聚合
kset (对象集合,如 "platform" 下的所有设备)
    ↓ 聚合
subsystem (子系统,如 /sys/bus/platform/)
    ↓ 导出
sysfs (用户空间可读的目录/文件树)

2.2 sysfs:设备模型的用户空间镜像

sysfs 是 ldm 的可视化出口,将内核对象树导出为 /sys 下的文件系统。

路径含义内核版本
/sys/devices/设备的物理拓扑树(真实硬件层次)2.6+
/sys/bus/总线类型组织的设备和驱动2.6+
/sys/class/功能类别组织的设备(功能视图)2.6+
/sys/dev/按主/次设备号组织的快捷方式2.6+
/sys/firmware/设备树(dtb)和 acpi 表3.7+ (dt)

关键洞察class 回答"这是什么"(功能视图),bus 回答"它挂在哪"(连接视图),devices 回答"它在主板上的位置"(物理视图)。三者通过符号链接指向同一个底层 device 对象。

2.3 sysfs 底层重构:kernfs

变更版本:linux 3.14(2014 年) 

作者:greg kroah-hartman

在 3.14 之前,sysfs 是一个独立的文件系统实现。3.14 将其底层逻辑提取为 kernfs(kernel filesystem),sysfs 成为 kernfs 的一个实例。这一重构提升了 sysfs 的性能和可维护性,但用户空间视角完全不变

三、总线-设备-驱动(bdd)三元架构

定型版本:linux 2.6(2003 年) 

核心机制bus_type 作为撮合器,match() 匹配,probe() 初始化。

3.1 三大核心结构体

/* 总线:连接器和仲裁器 */
struct bus_type {
    const char *name;                          /* 如 "platform", "i2c", "usb" */
    int (*match)(struct device *dev, struct device_driver *drv);
    int (*probe)(struct device *dev);
    int (*remove)(struct device *dev);
    struct subsys_private *p;                  /* 私有数据:管理链表 */
};

/* 设备:硬件的抽象 */
struct device {
    struct kobject kobj;                       /* 继承 kobject */
    struct device *parent;                     /* 父设备(物理拓扑) */
    struct bus_type *bus;                      /* 所属总线 */
    struct device_driver *driver;            /* 绑定的驱动 */
    void *platform_data;                       /* 平台私有数据 */
    struct device_node *of_node;               /* 设备树节点 */
};

/* 驱动:操作的抽象 */
struct device_driver {
    const char *name;
    struct bus_type *bus;
    int (*probe)(struct device *dev);
    int (*remove)(struct device *dev);
    const struct of_device_id *of_match_table; /* 设备树匹配表 */
};

3.2 匹配(match)机制

当设备或驱动注册到总线时,总线执行 match() 函数。以 platform 总线为例,匹配优先级(从高到低):

1. driver_override(设备强制指定驱动)
2. of 匹配(设备树 compatible vs 驱动 of_match_table)  ← 现代主流(3.7+)
3. acpi 匹配(x86 平台)
4. id_table 匹配(platform_device_id 数组)
5. name 匹配(pdev->name == drv->name)  ← 2.6 时代遗留

匹配成功后,总线调用驱动的 probe() 函数,驱动正式接管设备。

3.3 注册与调用链

设备注册: device_register()
    → 添加到 bus->devices 链表
    → 遍历 bus->drivers 尝试 match

驱动注册: driver_register()
    → 添加到 bus->drivers 链表
    → 遍历 bus->devices 尝试 match

匹配成功: bus->match() == true
    → driver->probe(dev)
    → 设备初始化完成

四、设备管理子系统

4.1 设备节点(/dev)

linux 贯彻"一切皆文件"。设备在内核中注册后,需要在 /dev 下创建节点供用户空间访问。

设备类型特点典型代表核心结构体
字符设备按字节流顺序访问,无缓存/dev/ttys0, /dev/i2c-0struct cdev
块设备按块随机访问,有页缓存/dev/sda, /dev/mmcblk0struct gendisk
网络设备无文件节点,通过 socket 访问eth0, wlan0struct net_device

设备号dev_t = 主设备号(12bit) + 次设备号(20bit),唯一标识驱动实例。

4.2 devtmpfs —— 早期设备节点

引入版本:linux 2.6.32(2009 年)

devtmpfs 之前,/dev 需要 udev 在启动后遍历 /sys 创建节点,启动较慢。devtmpfs 是一个挂载在 /dev 的 tmpfs,由内核在设备注册时自动创建基本节点udev 随后修改权限/符号链接。这显著缩短了启动时间。

4.3 udev —— 动态设备管理器

引入版本:linux 2.6.13(2005 年),替代 devfs 

机制:用户空间守护进程,监听内核通过 netlink 发送的 uevent 事件。

事件类型触发时机udev 动作
add设备注册到内核创建/调整 /dev/xxx 节点、设置权限、加载固件
remove设备注销删除节点
change设备状态变化更新属性
bind驱动绑定设备创建符号链接
unbind驱动解绑移除符号链接

uevent 机制:内核通过 kobject_uevent() 发送事件,携带环境变量(devname, modalias, of_name 等),udev 据此匹配 /etc/udev/rules.d/ 下的规则文件。

4.4 电源管理

成熟版本:linux 2.6.30+(dev_pm_ops 标准化)

通过 struct dev_pm_ops 挂接 suspend/resume 回调,与设备模型绑定,实现系统级睡眠/唤醒。驱动只需实现 suspend/resume 方法,内核自动按设备树拓扑顺序调用。

五、设备驱动分类与架构

5.1 按设备类型分类

类型核心结构体注册函数用户空间接口定型版本
字符设备struct cdevalloc_chrdev_region() + cdev_add()open/read/write/ioctl2.6
块设备struct gendisk + struct request_queueregister_blkdev() + add_disk()read/write(vfs/页缓存)2.6
网络设备struct net_deviceregister_netdev()socket 接口2.6

5.2 按总线类型分类

总线控制器结构体设备结构体驱动结构体匹配依据定型版本
platformstruct platform_device同左struct platform_drivername / compatible / id_table2.6
i2cstruct i2c_adapterstruct i2c_clientstruct i2c_driverof_match_table / id_table2.6
spistruct spi_masterstruct spi_devicestruct spi_driverof_match_table / modalias2.6
usbstruct usb_hcdstruct usb_interfacestruct usb_driverid_table(vid/pid)2.4
pcistruct pci_busstruct pci_devstruct pci_driverid_table(vendor/device id)2.2

5.3 驱动分层框架

上图:linux 驱动从用户空间到硬件的六层调用栈。自上向下依次为 用户空间 → vfs/socket 层 → core kernel → 总线框架 → 设备驱动 → bsp/hal,箭头表示调用/数据流向。

六、平台设备与设备树

6.1 platform 总线 —— 嵌入式 soc 的虚拟总线

soc 内部大量外设(uart、gpio、timer)没有物理总线(非 i2c/spi/usb),linux 引入 platform 作为虚拟总线来承载它们。

/* platform 设备:描述硬件资源 */
struct platform_device {
    const char *name;
    int id;
    struct device dev;
    u32 num_resources;
    struct resource *resource;   /* 寄存器基地址、中断号、dma 等 */
};

/* platform 驱动:实现操作逻辑 */
struct platform_driver {
    int (*probe)(struct platform_device *);
    int (*remove)(struct platform_device *);
    struct device_driver driver;
    const struct platform_device_id *id_table;
};

6.2 设备树(device tree)—— 硬件描述的标准化

arm 强制采用版本:linux 3.7(2012 年) 

核心思想:将硬件信息从内核代码中剥离,实现"一个内核镜像适配多个硬件平台"。

/* arch/arm64/boot/dts/xxx.dts */
my_uart: serial@12340000 {
    compatible = "vendor,my-uart";   /* 匹配驱动的关键 */
    reg = <0x12340000 0x100>;       /* 寄存器基址 + 大小 */
    interrupts = <gic_spi 10 irq_type_level_high>;
    clocks = <&clk_uart>;
    status = "okay";
};

匹配链

设备树节点(.dts)
    ↓ 编译
设备树 blob(.dtb)
    ↓ 内核启动时解析
platform_device(含 of_node 指针)
    ↓ compatible 属性匹配
platform_driver.of_match_table
    ↓ 匹配成功
driver->probe()

6.3 devm_* 资源管理 api

引入版本:linux 2.6.30(2009 年) 

作者:tejun heo

devm_* 系列 api(devm_kzalloc, devm_ioremap_resource, devm_clk_get, devm_request_irq 等)与 struct device 生命周期绑定,当设备被移除或驱动卸载时,内核自动释放这些资源,彻底避免内存泄漏。

对比

传统 apidevm_* api优势
kmalloc()devm_kzalloc()自动释放,无需 kfree()
ioremap()devm_ioremap_resource()自动 iounmap(),含资源冲突检查
request_irq()devm_request_irq()自动 free_irq()
clk_get()devm_clk_get()自动 clk_put()

七、内核版本演进与关键里程碑

7.1 时间线总览

linux 2.4 (2001)
    ├── usb 总线框架成熟
    └── pci 热插拔支持

linux 2.5/2.6 (2003)
    ├── kobject / kset / sysfs 引入 ← 设备模型定型
    ├── bus_type / device / device_driver 三元模型确立
    ├── udev 替代 devfs
    └── platform 总线引入

linux 2.6.16 (2006)
    └── device tree 正式支持 powerpc

linux 2.6.30 (2009)
    ├── devm_* 资源管理 api 引入
    └── devtmpfs 引入(2.6.32 完善)

linux 3.7 (2012)
    └── arm 架构强制使用 device tree ← 嵌入式范式变革

linux 3.10 (2013)
    ├── sysfs 底层重构为 kernfs
    └── platform + of 深度整合,现代驱动范式成熟

linux 3.14 (2014)
    └── sysfs → kernfs 重构完成

linux 4.x (2015-2018)
    ├── acpi + of 双架构并行成熟
    └── 驱动模型 api 完全稳定

linux 5.x - 6.x (2019-至今)
    ├── 核心框架无结构性变动
    ├── 子系统驱动大规模扩展
    └── rust 驱动支持实验性引入(6.1+)

7.2 关键版本对照表

机制首次引入成熟/强制版本当前状态
kobject/kset/sysfs2.52.6沿用至今,未变
bus_type/device/driver 匹配2.52.6沿用至今,未变
platform 总线2.62.6沿用至今
udev 替代 devfs2.6.132.6.15沿用至今
devm_* 资源管理2.6.303.10+现代驱动标配
devtmpfs2.6.323.0+现代发行版标配
device tree (powerpc)2.6.162.6.30沿用至今
device tree (arm 强制)3.7arm 嵌入式唯一标准
kernfs 重构3.143.14底层实现,用户无感知
module_platform_driver() 宏2.6.293.10+现代驱动标配
of_match_table 匹配2.6.303.7+设备树时代标配
rust 驱动支持6.1实验性演进中

7.3 不同版本内核的开发差异

linux 2.6.x( legacy 嵌入式)

  • 无设备树:硬件信息写在 arch/arm/mach-xxx/board-xxx.c
  • 无 devm_*:需要手动 iounmap / free_irq / clk_put
  • 匹配方式platform_device_id 或纯 name 字符串对比
  • 注册方式:手动 platform_driver_register() / platform_driver_unregister()

linux 3.10+(现代嵌入式)

  • 设备树of_match_table + compatible 是标准匹配方式
  • devm_*:资源自动释放,驱动代码大幅简化
  • 宏注册module_platform_driver() 一行完成注册/注销
  • 现代 apiplatform_get_irq() / platform_get_resource() 替代手动解析

linux 5.x - 6.x(当前主线)

  • 核心框架:与 3.10+ 完全一致
  • 新增特性:bpf 子系统扩展、rust 驱动实验、更完善的电源管理
  • 驱动开发:与 3.10+ 范式无异,可直接复用

八、驱动开发最小骨架

以下是一个符合 linux 设备模型、兼容 linux 3.10+ 至 6.x 的完整驱动模板。

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>

/* ========== 1. 设备树匹配表 ========== */
static const struct of_device_id my_of_match[] = {
    { .compatible = "vendor,mydev" },
    { }
};
module_device_table(of, my_of_match);

/* ========== 2. 文件操作接口 ========== */
static int my_open(struct inode *inode, struct file *filp)
{
    return 0;
}

static int my_release(struct inode *inode, struct file *filp)
{
    return 0;
}

static ssize_t my_read(struct file *filp, char __user *buf,
                       size_t len, loff_t *off)
{
    return 0;
}

static const struct file_operations my_fops = {
    .owner   = this_module,
    .open    = my_open,
    .release = my_release,
    .read    = my_read,
};

/* ========== 3. 平台驱动 probe/remove ========== */
static int my_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    struct resource *res;
    void __iomem *base;
    int irq, ret;

    dev_info(dev, "device probed!\n");

    /* 获取寄存器资源 */
    res = platform_get_resource(pdev, ioresource_mem, 0);
    base = devm_ioremap_resource(dev, res);
    if (is_err(base))
        return ptr_err(base);

    /* 获取中断号 */
    irq = platform_get_irq(pdev, 0);
    if (irq < 0)
        return irq;

    /* 获取时钟 */
    struct clk *clk = devm_clk_get(dev, null);
    if (is_err(clk))
        return ptr_err(clk);
    clk_prepare_enable(clk);

    /* 注册字符设备(示例) */
    // alloc_chrdev_region, cdev_init, cdev_add, device_create...

    return 0;
}

static int my_remove(struct platform_device *pdev)
{
    dev_info(&pdev->dev, "device removed!\n");
    /* devm_* 资源会自动释放,无需手动清理 */
    return 0;
}

/* ========== 4. 平台驱动结构体 ========== */
static struct platform_driver my_driver = {
    .driver = {
        .name           = "my_driver",
        .of_match_table = my_of_match,
    },
    .probe  = my_probe,
    .remove = my_remove,
};

/* ========== 5. 模块入口(自动注册/注销) ========== */
module_platform_driver(my_driver);

module_license("gpl");
module_author("embedded dev");
module_description("linux device model demo driver");

九、附录:核心 api 速查表

a. kobject / sysfs api

api功能版本
kobject_init_and_add()初始化并注册 kobject2.6+
kobject_put()释放引用计数2.6+
kobject_uevent()发送 uevent 事件2.6+
sysfs_create_group()创建 sysfs 属性组2.6+

b. 设备注册 api

api功能版本
device_register()注册设备到内核2.6+
device_unregister()注销设备2.6+
driver_register()注册驱动2.6+
driver_unregister()注销驱动2.6+
bus_register()注册总线2.6+

c. platform 驱动 api

api功能版本
platform_driver_register()注册 platform 驱动2.6+
module_platform_driver()自动注册/注销宏2.6.29+
platform_get_resource()获取设备资源2.6+
platform_get_irq()获取中断号2.6+
devm_ioremap_resource()安全映射寄存器3.5+
devm_clk_get()获取时钟(自动释放)3.6+
devm_request_irq()注册中断(自动释放)2.6.30+

d. 字符设备 api

api功能版本
alloc_chrdev_region()动态分配设备号2.6+
register_chrdev_region()静态分配设备号2.6+
cdev_init()初始化 cdev2.6+
cdev_add()添加 cdev 到内核2.6+
device_create()创建 /dev 节点2.6+
class_create()创建 class2.6+

参考资源:

  1. 《linux device drivers, 3rd edition》 — jonathan corbet, alessandro rubini, greg kroah-hartman(经典教材,覆盖 2.6 内核)
  2. 《essential linux device drivers》 — sreekrishnan venkateswaran
  3. kernel documentationdocumentation/driver-api/(内核源码内文档)
  4. elinux wiki:https://elinux.org/device_tree(设备树专题)

文档说明

本文档基于 linux 2.6+ 核心框架与 linux 3.10+ 现代驱动范式分析,兼容当前 linux 6.x 主线内核。

对于维护 linux 2.6 legacy 内核的项目,需注意设备树和 devm_* api 的缺失,回退到传统 platform_resource 和手动资源管理方案。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com