Linux 设备模型、设备管理与设备驱动

Linux 设备模型、设备管理与设备驱动

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


目录

  1. 概述与核心设计哲学
  2. Linux 设备模型(LDM)
  3. 总线-设备-驱动(BDD)三元架构
  4. 设备管理子系统
  5. 设备驱动分类与架构
  6. 平台设备与设备树
  7. 内核版本演进与关键里程碑
  8. 驱动开发最小骨架
  9. 附录:核心 API 速查表

一、概述与核心设计哲学

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 和手动资源管理方案。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值