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

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

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


目录

  1. 概述与核心设计哲学
  2. [Linux 设备模型(LDM)](#Linux 设备模型(LDM))
  3. 总线-设备-驱动(BDD)三元架构
  4. 设备管理子系统
  5. 设备驱动分类与架构
  6. 平台设备与设备树
  7. 内核版本演进与关键里程碑
  8. 驱动开发最小骨架
  9. [附录:核心 API 速查表](#附录:核心 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, ktype Linux 2.5
struct kset kobject 的容器 list(对象链表),内嵌 kobj Linux 2.5
struct ktype 定义对象行为 release(), sysfs_ops Linux 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 三大核心结构体

c 复制代码
/* 总线:连接器和仲裁器 */
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-0 struct cdev
块设备 按块随机访问,有页缓存 /dev/sda, /dev/mmcblk0 struct gendisk
网络设备 无文件节点,通过 socket 访问 eth0, wlan0 struct 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 cdev alloc_chrdev_region() + cdev_add() open/read/write/ioctl 2.6
块设备 struct gendisk + struct request_queue register_blkdev() + add_disk() read/write(VFS/页缓存) 2.6
网络设备 struct net_device register_netdev() socket 接口 2.6

5.2 按总线类型分类

总线 控制器结构体 设备结构体 驱动结构体 匹配依据 定型版本
Platform struct platform_device 同左 struct platform_driver name / compatible / id_table 2.6
I2C struct i2c_adapter struct i2c_client struct i2c_driver of_match_table / id_table 2.6
SPI struct spi_master struct spi_device struct spi_driver of_match_table / modalias 2.6
USB struct usb_hcd struct usb_interface struct usb_driver id_table(VID/PID) 2.4
PCI struct pci_bus struct pci_dev struct pci_driver id_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 作为虚拟总线来承载它们。

c 复制代码
/* 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 年)

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

dts 复制代码
/* 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 生命周期绑定,当设备被移除或驱动卸载时,内核自动释放这些资源,彻底避免内存泄漏。

对比

传统 API devm_* 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/sysfs 2.5 2.6 沿用至今,未变
bus_type/device/driver 匹配 2.5 2.6 沿用至今,未变
platform 总线 2.6 2.6 沿用至今
udev 替代 devfs 2.6.13 2.6.15 沿用至今
devm_* 资源管理 2.6.30 3.10+ 现代驱动标配
devtmpfs 2.6.32 3.0+ 现代发行版标配
Device Tree (PowerPC) 2.6.16 2.6.30 沿用至今
Device Tree (ARM 强制) --- 3.7 ARM 嵌入式唯一标准
kernfs 重构 3.14 3.14 底层实现,用户无感知
module_platform_driver() 2.6.29 3.10+ 现代驱动标配
of_match_table 匹配 2.6.30 3.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 的完整驱动模板。

c 复制代码
#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() 初始化并注册 kobject 2.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() 初始化 cdev 2.6+
cdev_add() 添加 cdev 到内核 2.6+
device_create() 创建 /dev 节点 2.6+
class_create() 创建 class 2.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 Wikihttps://elinux.org/Device_Tree(设备树专题)

文档说明 :本文档基于 Linux 2.6+ 核心框架与 Linux 3.10+ 现代驱动范式分析,兼容当前 Linux 6.x 主线内核。对于维护 Linux 2.6 legacy 内核的项目,需注意设备树和 devm_* API 的缺失,回退到传统 platform_resource 和手动资源管理方案。

相关推荐
六点_dn6 小时前
Linux学习笔记-shell运算符
linux·笔记·学习
hehelm7 小时前
IO 多路复用 — Reactor
linux·服务器·网络·数据库·c++
浅止菌7 小时前
嵌入式项目Makefile越写越乱?一张万能模板+5个技巧,告别重复劳动
linux
刘某的Cloud8 小时前
手工配置nginx的systemd服务
linux·运维·网络·nginx·systemd
FREEDOM_X9 小时前
嵌入式Linux摄像头采集与图像处理
linux·图像处理·ubuntu
studytosky9 小时前
OpenClaw 入门与 Skill 开发
linux·服务器·ai编程
kaoa0009 小时前
Linux入门攻坚——82、kvm虚拟化-2
linux·运维·服务器
学无止境_永不停歇10 小时前
3. 进程状态
linux·服务器
刘某的Cloud10 小时前
Nginx 最小安全配置模板
linux·运维·nginx·安全·系统
kdxiaojie11 小时前
Linux 驱动研究 —— V4L2 (2)
linux·运维·笔记·学习