news 2026/6/8 7:29:42

字符设备、class 和 kobject 之间的关系

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
字符设备、class 和 kobject 之间的关系

【class/device/cdev】

在 Linux 设备模型中,**字符设备 (cdev)**、**class** 和 **kobject** 分别属于**不同层次的概念**,但它们会协同工作,共同构建一个完整的、可在用户空间访问的设备。

| 概念 | 所属子系统 | 核心作用 | 用户空间可见 |
|------|-----------|----------|--------------|
| **kobject** | 设备模型基础 | 提供引用计数、父子关系、sysfs 入口 | 在 `/sys/` 下表现为一个目录 |
| **class** | 设备模型高级抽象 | 按功能对设备进行分组(如 `tty`, `input`) | 在 `/sys/class/<class_name>/` 下 |
| **device** | 设备模型核心 | 代表一个具体的硬件设备,内嵌 `kobject` | 在 `/sys/devices/` 以及 `/sys/class/...` 下作为符号链接 |
| **cdev** | 字符设备子系统 | 关联设备号与 `file_operations`,提供读写接口 | 不直接可见,但通过 `/dev` 节点间接访问 |

---

### 🧬 关系图解

```
┌─────────────────────────────────────────────────────────────────┐
│ Linux 设备模型 │
├─────────────────────────────────────────────────────────────────┤
│ kobject (基础) │
│ ├── 引用计数、sysfs 表示 │
│ └── 嵌入于:class, device, cdev ... │
├─────────────────────────────────────────────────────────────────┤
│ class device │
│ (例如 "my_class") (例如 "my_device") │
│ │ │ │
│ └── 包含多个 device ────────────┘ │
│ device 也属于某个 bus、parent 等 │
├─────────────────────────────────────────────────────────────────┤
│ cdev (字符设备) │
│ ├── 本身包含 kobject │
│ ├── 通过设备号 (dev_t) 与 device 隐式关联 │
│ └── 提供 file_operations (open/read/write) │
└─────────────────────────────────────────────────────────────────┘


用户空间访问路径:
/sys/class/my_class/my_device/ (class/device 信息)
/dev/my_device (设备节点,由 devtmpfs 创建)
```

---

### 🔗 三者之间的协作关系

#### 1. kobject 是所有设备模型对象的根基

`struct kobject` 被嵌入在 `class`、`device`、`cdev` 等结构体中,使它们具有以下能力:
- 引用计数管理(生命周期)
- 在 sysfs 中呈现为目录
- 通过 `parent` 指针建立层次关系

```c
struct class {
struct kobject kobj; // 使得 /sys/class/xxx 出现
...
};

struct device {
struct kobject kobj; // 使得 /sys/devices/... 出现
struct class *class; // 指向所属 class
dev_t devt; // 设备号(若有)
...
};

struct cdev {
struct kobject kobj; // cdev 本身也可以有 sysfs 表示(如 /sys/module/.../holders)
const struct file_operations *ops;
dev_t dev;
unsigned int count;
};
```

#### 2. class 是设备的“分组标签”

- 一个 `class` 代表一类设备(例如 `"tty"`, `"gpio"`, `"input"`)。
- 通过 `class_create()` 创建,在 `/sys/class/<class_name>/` 下生成目录。
- `device` 可以通过 `device_create(class, ...)` 加入某个 class,此时 `/sys/class/<class_name>/<device_name>/` 会创建一个指向真实设备的符号链接。

#### 3. device 是硬件实体在软件中的抽象

- `device` 结构体描述了一个具体的硬件(或虚拟设备)。
- 它包含:
- `devt`(设备号,如果是字符设备)
- `class`(所属类)
- `parent`(父设备,用于构建设备树)
- `kobj`(sysfs 入口)
- 当驱动调用 `device_create()` 时:
- 在 `/sys/devices/...` 下创建设备目录。
- 如果有 `class`,在 `/sys/class/<class>/<device>/` 下创建符号链接。
- 如果 `devt` 有效(即提供了设备号),会触发 `devtmpfs` 在 `/dev/` 下创建对应的设备节点文件。

#### 4. cdev 是字符设备驱动与内核的“注册凭证”

- `cdev` 负责将**设备号范围**与 **`file_operations`** 绑定。
- 当应用程序 `open("/dev/mydevice")` 时:
- VFS 根据 `inode->i_rdev` 得到设备号。
- 内核通过全局哈希表(`cdev_map`)查找该设备号对应的 `cdev`。
- 获得 `cdev->ops` 并替换 `filp->f_op`。
- **cdev 并不直接关联 class 或 device**,但是:
- 一个常见的做法是:先分配设备号 → 注册 `cdev` → 调用 `device_create(..., devt, ...)`。
- 这样 `device` 拥有了设备号,而 `device_create` 内部会设置 `device->devt = devt`,并调用 `devtmpfs_create_node()` 创建设备节点。
- 因此,虽然 `cdev` 和 `device` 结构分离,但它们通过**共同的设备号**建立起隐式联系。

---

### 🧩 典型驱动初始化流程(结合三者)

```c
static dev_t my_devno;
static struct cdev my_cdev;
static struct class *my_class;
static struct device *my_device;

static int __init my_init(void)
{
// 1. 分配设备号
alloc_chrdev_region(&my_devno, 0, 1, "mydev");

// 2. 初始化 cdev 并添加(字符设备接口)
cdev_init(&my_cdev, &my_fops);
my_cdev.owner = THIS_MODULE;
cdev_add(&my_cdev, my_devno, 1);

// 3. 创建 class(/sys/class/myclass)
my_class = class_create(THIS_MODULE, "myclass");

// 4. 创建 device(/sys/class/myclass/mydevice,以及 /dev/mydevice)
my_device = device_create(my_class, NULL, my_devno, NULL, "mydevice");

return 0;
}
```

> ✅ 执行 `device_create` 时,会利用传入的 `my_devno` 在 `devtmpfs` 中创建 `/dev/mydevice` 节点。
> ✅ 当用户 `open("/dev/mydevice")`,内核能够根据设备号 `my_devno` 找到已注册的 `my_cdev`,从而调用 `my_fops` 中的函数。

---

### 🧠 总结:它们的关系是“分工协作,部分解耦”

| 对象 | 职责 | 与其他对象的联系 |
|------|------|------------------|
| **kobject** | 设备模型的基础构件,提供 sysfs 和引用计数 | 被 `class`、`device`、`cdev` 嵌入 |
| **class** | 按功能分组设备,组织 `/sys/class` 目录 | 通过 `device->class` 与 `device` 关联 |
| **device** | 代表一个真实或虚拟硬件,管理电源、休眠等 | 通过 `devt` 与 `cdev` 隐式关联;通过 `class` 参与分类 |
| **cdev** | 提供字符设备的 `file_operations` 接口 | 通过设备号(`dev_t`)与 `device` 隐式关联;不直接依赖 `class` |

> 关键理解:**字符设备的接口能力(cdev)与设备的分类/生命周期管理(class/device)是正交的**,它们通过设备号这座“桥梁”连接。这也是为什么你可以注册一个字符设备(`cdev_add`)而不创建 `class`/`device`,但那样就不会自动在 `/dev` 下生成节点(需要手动 `mknod`)。现代驱动推荐二者结合,获得自动节点管理和 sysfs 属性。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/8 7:27:14

从YAML.load到Hydra:我的Python项目配置管理升级踩坑实录

从YAML.load到Hydra&#xff1a;我的Python项目配置管理升级踩坑实录记得三年前刚接手第一个Python数据分析项目时&#xff0c;配置管理简单得令人发笑——一个config.yaml文件加上几行yaml.load()代码就搞定了。但随着项目规模像吹气球一样膨胀&#xff0c;这个看似优雅的方案…

作者头像 李华
网站建设 2026/6/8 7:27:00

Python一行代码生成杨辉三角?聊聊背后的几种实现与性能对比

Python一行代码生成杨辉三角&#xff1f;聊聊背后的几种实现与性能对比杨辉三角这个看似简单的数学结构&#xff0c;在编程领域却像一面多棱镜&#xff0c;能折射出不同编程范式的独特光芒。作为Python开发者&#xff0c;我们常常被这门语言的简洁性所吸引——那些用一行代码就…

作者头像 李华
网站建设 2026/6/8 7:23:15

从收货到清空:一张图看懂SAP WM仓储单位(SU)的完整生命周期与管理要点

从收货到清空&#xff1a;一张图看懂SAP WM仓储单位(SU)的完整生命周期与管理要点在现代化仓储管理中&#xff0c;SAP WM系统的仓储单位(Storage Unit, SU)扮演着核心角色。它不仅是库存移动的载体&#xff0c;更是实现精细化管理的数字纽带。对于刚接触SAP WM的操作员或需要快…

作者头像 李华