基于内核版本: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, 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 三大核心结构体
/* 总线:连接器和仲裁器 */
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 作为虚拟总线来承载它们。
/* 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 生命周期绑定,当设备被移除或驱动卸载时,内核自动释放这些资源,彻底避免内存泄漏。
对比:
| 传统 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 的完整驱动模板。
#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 和手动资源管理方案。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论