09、设备模型简介
一、设备模型简介
设备模型是操作系统管理硬件的核心工具,它通过四个简单概念让硬件管理和驱动开发变得更轻松:
- 总线(Bus):就像连接设备的"数据高速公路"。常见的有物理总线(如PCI插槽、USB接口),也有虚拟总线(比如管理虚拟设备的内部通道)。它们负责在设备间传递数据。
- 设备(Device):就是计算机里的各种硬件,比如键盘、显示器、网卡等。每个设备都有独一无二的"身份证号",系统通过这个ID来识别和管理它。设备的信息会用标准化的形式记录下来。
- 驱动(Driver):这是控制硬件的软件程序。每个硬件都需要对应的驱动程序,就像硬件的"翻译官",负责把操作系统指令转化为设备能理解的语言,同时接收设备反馈的信息。
- 设备类(Class):按功能把设备分门别类。比如所有声卡都属于"音频设备类",摄像头属于"视频设备类"。这样系统能统一管理同类设备,方便统一配置和使用。
设备模型的作用就像一个标准化的"设备管理超市":
- 统一格式:所有设备、驱动都用相同方式描述
- 简化开发:开发者不用重复造轮子,可复用已有代码
- 动态管理:支持热插拔(边用边插拔设备),自动识别新硬件
- 兼容性强:不同品牌/型号的同类设备能用相似方式管理
通过这四个基础概念,设备模型把复杂的硬件世界变得有条不紊,让操作系统能高效安全地与各种硬件设备打交道。
二、相关结构体
Linux系统里有个叫"platform总线"的虚拟接口,专门用来管理那些直接连接CPU但又不按PCI、USB等标准工作的硬件设备。
这些设备的信息会提前写在设备树文件里。当驱动程序运行时,它会通过设备树找到对应的硬件。platform总线为这些设备提供统一的方法,让驱动能方便地:
1️⃣ 注册到系统;
2️⃣ 自动绑定到对应的硬件;
3️⃣ 直接操作硬件的设置(比如读写寄存器、配置参数);
4️⃣ 处理硬件触发的中断信号。
这样,驱动就能直接和硬件"对话",完成对硬件的控制操作。
以下是更直白的改写版本:
设备、驱动、总线、类,这四个概念就像硬件和软件的分工合作:
- 设备是硬件本身(比如摄像头、内存芯片)。
- 驱动是让硬件工作的软件程序(比如摄像头如何拍照、内存如何存数据)。
- 总线是连接处理器和设备的通道(比如数据传输的"电路"或"数据高速路")。
- 类是同类设备的通用管理规则(比如所有USB设备都遵循USB类的统一规则)。
2.1、bus_type 总线
bus_type 结构体是 Linux 内核中用于描述总线的数据结构,定义在 include/linux/device.h 头文件中。以下是对 bus_type 结构体的一般定义。
bus_type
+------------------------------------+
|name |
|dev_name |
| (char *) |
+------------------------------------+
|bus_groups |
|dev_groups |
|drv_groups |
| (struct attribute_group**) |
+------------------------------------+
|match |
|uevent |
|probe |
|remove |
|shutdown |
| |
+------------------------------------+
|p |
| (struct subsys_private*) |
| +--------------------------------+
| |bus |
| | (struct bus_type*) |
| |subsys |
| | +----------------------------+
| | |kobj.name | = bus->name
| | |kobj.kset | = bus_kset
| | |kobj.ktype | = bus_ktype
| | +----------------------------+
| |devices_kset |
| |drivers_kset |
| | (struct kset) |
| |klist_devices |
| |klist_drivers |
| | (struct klist) |
| |class |
| | (struct class*) |
+---+--------------------------------+
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
结构体成员说明
- name:总线类型的名称(比如PCI、USB等)。
- dev_name:总线设备的名字(标识具体设备)。
- dev_root:总线设备的根设备(总线上的最顶层设备)。
- bus_groups:总线类型的属性集合(用于管理总线相关参数)。
- dev_groups:设备的属性集合(用于管理设备相关参数)。
- drv_groups:驱动程序的属性集合(用于管理驱动相关参数)。
回调函数成员说明
- match:判断设备和驱动是否匹配的函数。
- uevent:处理设备事件(如状态变化)的函数。
- probe:当设备被检测到时,驱动执行的初始化操作。
- sync_state:同步设备当前状态的函数。
- remove:移除设备时执行的清理操作。
- online/offline:设备上线(启用)或下线(禁用)时的处理函数。
- suspend/resume:设备进入休眠或恢复运行时的处理函数。
- num_vf:返回设备支持的虚拟功能数量。
- dma_configure:配置设备DMA(直接内存访问)功能的函数。
其他成员说明
- pm:管理设备电源状态的设置(如省电模式)。
- iommu_ops:管理设备与内存安全通信的操作(IOMMU是输入输出内存管理单元)。
- p:存储子系统私有数据(供内部使用)。
- lock_key:用于锁机制的唯一标识(防止多线程冲突)。
- need_parent_lock:是否需要先锁定父设备才能操作当前设备。
每个成员的作用都简化为直接描述其功能或用途,避免技术术语堆砌,方便快速理解。
2.2、device (外设)
内核中用结构体device
来表示一个设备。不过大家通常在驱动程序中看到是另外的结构体,比如Platform
设备用的是platform_device
。但是我们打开这个platform_device
就可以看到其中包含了device
。所以设备的核心数据结构还是device
。
device
+----------------------------------------+
|init_name |
| (char *) |
|devt |
| (dev_t) |
|id |
| (u32) |
+----------------------------------------+
|kobj |
| (struct kobject) |
| +-----------------------------------+
| |name | = device->init_name or
| | | device->bus->dev_name + id
| |kset | = devices_kset
| |ktype | = device_ktype
+----+-----------------------------------+
|type |
| (struct device_type*) |
+----------------------------------------+
|bus | = [pci_bus_type|nvdimm_bus_type]
| (struct bus_type) |
+----------------------------------------+
|driver |
| (struct device_driver*) |
+----------------------------------------+
|p |
| (struct device_private *) |
+----------------------------------------+
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
各个成员含义:
- 设备名(init_name):设备的名称(如果有的话)
- 设备号(id):设备的唯一编号
- 总线(bus):设备所连接的总线类型(比如USB、PCI等)
- 驱动(driver):管理该设备的驱动程序
- 设备真实名称(kobj.name):内核中设备的正式名称
- 父节点(kobj.kset):设备在内核对象树中的上级节点
关键区别:
- 驱动的父节点:指向它所属的总线(比如USB驱动的父节点是USB总线)
- 设备的父节点:指向另一个根节点(比如系统级的设备根目录)
简单总结: 当设备和驱动都存在时,内核对象树的结构会这样展开:
- 驱动挂在总线下面
- 设备挂在系统根目录下
- 两者通过匹配关系关联,但属于不同的层级路径
这样设备和驱动在内核的组织结构中就像两条并行的"树",各自有独立的父节点,但能通过总线等机制相互连接。
2.3、device_driver (驱动)
内核中用结构体device_driver
来表示一个驱动。不过大家通常在驱动程序中看到是另外的结构体,比如Platform
驱动用的是platform_driver
。但是我们打开这个platform_driver
就可以看到其中包含了device_driver
。所以驱动的核心数据结构还是device_driver
。
device_driver
+----------------------------------+
|name |
| (char *) |
+----------------------------------+
|bus |
| (struct bus_type*) |
+----------------------------------+
|owner |
| (struct module*) |
+----------------------------------+
|probe |
|remove |
|shutdown |
|suspend |
|resume |
| |
+----------------------------------+
|p |
| (struct driver_private*) |
| +-----------------------------+
| |driver |
| | (struct device_driver*) |
| |kobj |
| | +------------------------+
| | |name | = device_driver->name
| | |kset | = device_driver->bus->p->drivers_kset
| | |ktype | = driver_ktype
| | +------------------------+
| |klist_devices |
| | |
+----+-----------------------------+
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
结构体成员说明
name:驱动程序的名称
bus:设备连接的总线类型(如USB、PCI等)
owner:拥有该驱动的模块(通常是内核模块)
mod_name:内置模块的名称
suppress_bind_attrs:禁用通过系统文件系统(sysfs)进行设备绑定和解绑的功能
probe_type:探测方式,决定如何检测设备
of_match_table:匹配Open Firmware设备的列表
acpi_match_table:匹配ACPI设备的列表
groups:属性的分组集合(用于组织设备信息)
pm:电源管理操作(如休眠、唤醒等)
p:驱动程序的私有数据(供内部使用)
回调函数说明
- probe:设备检测函数,用于初始化和配置设备。 条件:设备和驱动必须连接到同一总线才能触发此函数。
- sync_state:同步设备状态的函数(如恢复配置)
- remove:移除设备时调用的清理函数
- shutdown:设备关机时的处理函数
- suspend:设备进入休眠状态时的处理函数
- resume:设备从休眠恢复时的处理函数
- coredump:设备发生故障时的核心转储函数(记录错误信息)
关键点总结
- 总线匹配:设备和驱动必须挂载在同一个总线下,才能触发
probe
函数。 - 电源管理:
suspend
和resume
用于控制设备的低功耗状态。 - 属性管理:通过
groups
和sysfs
管理设备的属性和交互。
2.4、struct class
struct class 是 Linux 内核中描述设备类的数据结构,定义在 include/linux/device.h 头文件中。以下是 struct class 的一般定义:
struct module
*owner;
const struct attribute_group **class_groups;
const struct attribute_group **dev_groups;
struct kobject
*dev_kobj;
int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
char *(*devnode)(struct device *dev, umode_t *mode);
void (*class_release)(struct class *class);
void (*dev_release)(struct device *dev);
int (*shutdown_pre)(struct device *dev);
const struct kobj_ns_type_operations *ns_type;
const void *(*namespace)(struct device *dev);
void (*get_ownership)(struct device *dev, kuid_t *uid, kgid_t *gid);
const struct dev_pm_ops *pm;
struct subsys_private *p;
ANDROID_KABI_RESERVE(1);
ANDROID_KABI_RESERVE(2);
ANDROID_KABI_RESERVE(3);
ANDROID_KABI_RESERVE(4);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
基础成员
- name:设备类的名称(比如"network"或"block")
- owner:拥有该设备类的内核模块(通常用
THIS_MODULE
表示)
属性组成员
- class_groups: 定义设备类的属性,在
/sys/class/设备类名/
目录下自动生成对应的属性文件。 - dev_groups: 定义设备的属性,在设备对应的目录(如
/sys/class/设备类名/设备名/
)下自动生成属性文件。 - dev_kobj: 设备的内核对象(内核管理设备的基础数据结构)
回调函数
- dev_uevent: 处理设备事件(如热插拔),当设备状态变化时被调用。
- devnode: 决定设备节点(如
/dev/设备名
)的创建方式和权限。
其他重要成员
- class_release: 设备类被删除时,释放类相关的资源。
- dev_release: 设备被删除时,释放设备相关的资源。
- shutdown_pre: 系统关机前调用的回调函数,用于清理操作。
- ns_type / namespace: 处理设备在不同命名空间(如用户空间)中的隔离和管理。
- get_ownership: 设置设备的用户/组权限(如
/dev/设备名
的属主)。 - pm: 电源管理功能(如设备休眠/唤醒的控制)。
- p: 指向设备类的私有数据(供模块自定义使用)
核心逻辑
- 属性组:类属性影响所有设备,设备属性针对具体设备。
- 回调函数:在特定事件发生时(如设备插入、删除、关机)自动触发。
- 释放函数:确保资源在设备/类被移除时正确释放,避免内存泄漏。