Linux 设备模型、设备管理与设备驱动
基于内核版本:Linux 2.6+(核心框架),Linux 3.10+(现代范式),Linux 6.x(当前主线)
目录
- 概述与核心设计哲学
- [Linux 设备模型(LDM)](#Linux 设备模型(LDM))
- 总线-设备-驱动(BDD)三元架构
- 设备管理子系统
- 设备驱动分类与架构
- 平台设备与设备树
- 内核版本演进与关键里程碑
- 驱动开发最小骨架
- [附录:核心 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()一行完成注册/注销 - 现代 API :
platform_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+ |
参考资源
- 《Linux Device Drivers, 3rd Edition》 --- Jonathan Corbet, Alessandro Rubini, Greg Kroah-Hartman(经典教材,覆盖 2.6 内核)
- 《Essential Linux Device Drivers》 --- Sreekrishnan Venkateswaran
- Kernel Documentation :
Documentation/driver-api/(内核源码内文档) - ELinux Wiki:https://elinux.org/Device_Tree(设备树专题)
文档说明 :本文档基于 Linux 2.6+ 核心框架与 Linux 3.10+ 现代驱动范式分析,兼容当前 Linux 6.x 主线内核。对于维护 Linux 2.6 legacy 内核的项目,需注意设备树和
devm_*API 的缺失,回退到传统platform_resource和手动资源管理方案。