第一百三十八章:设备模型
合道期涉及内核源码:
一
林小源在设备山脉的深处,发现了一块巨大的基石。
基石嵌入山体最深处,上面刻满了密密麻麻的字符——、、、、。每一道刻痕都散发着微弱的光芒,像是一张巨大的网络,把整个山脉中的所有设备都串联在一起。
"这是什么?"林小源蹲下来,用手指触摸那些刻痕。
基石突然震动了一下。一个低沉的、带着回响的声音从地底传来:
"我是 kobject。设备模型的基石。"
林小源吓得往后跳了一步。只见基石表面的刻痕缓缓亮起,浮现出一个半透明的人影。那人影通体漆黑,表面光滑如镜,没有五官,只有一双眼睛——眼睛里映着一个不断递增的数字:。
"那是引用计数。"kobject 说,"我的存在是为了两件事——第一,追踪每个内核对象的引用计数,确保没人用的时候自动释放;第二,在 sysfs 中创建对应的条目,让用户空间能看到内核世界里的设备。"
"你就是所有设备的基类?"
"对。"kobject 说,"device 结构体里嵌入了我。driver 结构体里也嵌入了我。kset 是我的集合体——把相关的 kobject 组织在一起,对应 sysfs 中的一个目录。"
/*
* 设备模型的核心结构:
*
* kobject:
* 内核对象的基类
* 引用计数、sysfs 表示
* 嵌入在其他结构中
*
* kset:
* kobject 的集合
* 对应 sysfs 中的目录
*
* device:
* 设备的抽象
* 包含 kobject
* 关联驱动、总线
*
* device_driver:
* 驱动的抽象
* 包含操作函数
*
* bus_type:
* 总线类型
* 管理设备和驱动的匹配
*/
printf("=== 设备模型 — 设备管理的框架 ===\n\n");
printf("设备模型的核心结构:\n\n");
printf("kobject:\n");
printf(" 内核对象的基类\n");
printf(" 引用计数\n");
printf(" sysfs 表示\n\n");
printf("kset:\n");
printf(" kobject 的集合\n");
printf(" 对应 sysfs 目录\n\n");
printf("device:\n");
printf(" 设备的抽象\n");
printf(" 包含 kobject\n");
printf(" 关联驱动、总线\n\n");
printf("--- 层次结构 ---\n");
printf(" bus_type\n");
printf(" ├── device 1\n");
printf(" │ └── kobject\n");
printf(" ├── device 2\n");
printf(" │ └── kobject\n");
printf(" └── device_driver\n\n");
printf("--- sysfs 对应 ---\n");
printf("/sys/\n");
printf("├── bus/\n");
printf("│ ├── pci/\n");
printf("│ └── usb/\n");
printf("├── devices/\n");
printf("│ └── pci0000:00/\n");
printf("└── class/\n");
printf(" └── net/\n\n");
printf("--- 设备注册 ---\n");
printf("device_register(&dev):\n");
printf(" 注册设备\n");
printf(" 创建 sysfs 条目\n");
printf(" 尝试匹配驱动\n\n");
printf("--- 驱动注册 ---\n");
printf("driver_register(&drv):\n");
printf(" 注册驱动\n");
printf(" 尝试匹配设备\n");#include <stdio.h>
/*
* 设备模型的核心结构:
*
* kobject:
* 内核对象的基类
* 引用计数、sysfs 表示
* 嵌入在其他结构中
*
* kset:
* kobject 的集合
* 对应 sysfs 中的目录
*
* device:
* 设备的抽象
* 包含 kobject
* 关联驱动、总线
*
* device_driver:
* 驱动的抽象
* 包含操作函数
*
* bus_type:
* 总线类型
* 管理设备和驱动的匹配
*/
int main() {
printf("=== 设备模型 — 设备管理的框架 ===\n\n");
printf("设备模型的核心结构:\n\n");
printf("kobject:\n");
printf(" 内核对象的基类\n");
printf(" 引用计数\n");
printf(" sysfs 表示\n\n");
printf("kset:\n");
printf(" kobject 的集合\n");
printf(" 对应 sysfs 目录\n\n");
printf("device:\n");
printf(" 设备的抽象\n");
printf(" 包含 kobject\n");
printf(" 关联驱动、总线\n\n");
printf("--- 层次结构 ---\n");
printf(" bus_type\n");
printf(" ├── device 1\n");
printf(" │ └── kobject\n");
printf(" ├── device 2\n");
printf(" │ └── kobject\n");
printf(" └── device_driver\n\n");
printf("--- sysfs 对应 ---\n");
printf("/sys/\n");
printf("├── bus/\n");
printf("│ ├── pci/\n");
printf("│ └── usb/\n");
printf("├── devices/\n");
printf("│ └── pci0000:00/\n");
printf("└── class/\n");
printf(" └── net/\n\n");
printf("--- 设备注册 ---\n");
printf("device_register(&dev):\n");
printf(" 注册设备\n");
printf(" 创建 sysfs 条目\n");
printf(" 尝试匹配驱动\n\n");
printf("--- 驱动注册 ---\n");
printf("driver_register(&drv):\n");
printf(" 注册驱动\n");
printf(" 尝试匹配设备\n");
return 0;
}二
"sysfs?"林小源想起了之前在用户空间见过的 /sys/ 目录。
kobject 的眼睛闪了闪:"你在用户空间看到的 /sys/,就是我的投影。每一个 kobject 在 sysfs 中都有一个目录——目录里有属性文件,显示设备的状态、配置、驱动信息。"
他伸手一挥,空中浮现出一棵树:
/sys/
├── bus/
│ ├── pci/
│ └── usb/
├── devices/
│ └── pci0000:00/
└── class/
└── net/"这棵树就是设备模型在用户空间的映射。"kobject 说,"/sys/bus/ 下是总线类型,/sys/devices/ 下是设备层次结构,/sys/class/ 下是设备类别。你通过读写这些文件,就能查询甚至控制设备。"
林小源盯着那棵树,忽然明白了:设备模型不仅仅是内核内部的数据结构——它还是内核和用户空间之间的桥梁。内核通过 sysfs 把设备信息暴露给用户空间,用户空间通过 sysfs 了解和管理设备。
三
"设备模型的核心是总线-设备-驱动的关系。"kobject 说。
他的身体裂开,露出内部的结构——一根根细线从他的核心向外延伸,连接着三种不同颜色的光团:蓝色的是总线,绿色的是设备,红色的是驱动。
"总线负责匹配。"kobject 说,"当一个新设备注册时——比如你插了一块网卡——总线会遍历所有已注册的驱动,用 函数检查每个驱动是否跟这个设备匹配。如果匹配上了,就调用驱动的 函数。"
"反过来呢?"
"反过来也一样。当一个新驱动注册时——比如你加载了一个内核模块——总线会遍历所有已注册的设备,找到匹配的设备,调用 probe。"
林小源看着那三种光团之间的连线,心中浮现出一个画面:设备注册时找驱动,驱动注册时找设备,总线在中间做媒人。这就是 Linux 设备模型的核心——一个自动匹配的框架。
"所以同一套驱动代码,"林小源说,"不管是先插设备还是先加载驱动,都能正常工作?"
kobject 点了点头:"这就是设备模型的优雅之处。"
道藏笔记
内核启示
设备模型是内核管理设备的框架。
核心结构:
- kobject — 内核对象基类
- kset — kobject 集合
- device — 设备抽象
- device_driver — 驱动抽象
- bus_type — 总线类型
总线-设备-驱动关系:
- 总线管理匹配
- 设备注册时匹配驱动
- 驱动注册时匹配设备
设备模型是"组织"的框架——让设备管理有序。
设备模型之试
设备模型完成 device 与 driver 匹配后,负责绑定并初始化设备的驱动回调是什么?