设备模型 开篇,先理解基本框架kobject和kset
前言
这里是kobject/kset 基础知识了解,也是设备模型开篇。设备模型可以让大家更好理解前面学习的知识,也可以为后面知识点做铺垫。
参考资料
一、Linux设备模型基本框架
Linux设备模型是内核提供的一个抽象层,用于描述系统中设备的层次结构和相互关系。其核心目的是:
- 统一设备表示和管理
- 支持设备的热插拔
- 提供用户空间接口(sysfs)
- 实现电源管理
- 支持设备驱动自动加载
设备模型的核心数据结构包括:
- kobject:基础对象,提供引用计数和sysfs接口
- kset:kobject的集合
- ktype:定义kobject的类型行为
- device:表示具体设备
- device_driver:表示设备驱动
- bus_type:表示总线类型
- class:设备分类
二、kobject详解
kobject 创建
kobject是设备模型中最基础的结构体,定义在<linux/kobject.h>中:
struct kobject {
const char *name; // 对象名称(sysfs中显示)
struct list_head entry; // 链表节点
struct kobject *parent; // 父对象
struct kset *kset; // 所属的kset
struct kobj_type *ktype; // 对象类型
struct kernfs_node *sd; // sysfs目录项
struct kref kref; // 引用计数
unsigned int state_initialized:1; // 初始化状态
};
看看代码示例:
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/configfs.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
// 定义了三个kobject指针变量:mykobject01、mykobject02、mykobject03
struct kobject *mykobject01;
struct kobject *mykobject02;
struct kobject *mykobject03;
// 定义了一个kobj_type结构体变量mytype,用于描述kobject的类型。
struct kobj_type mytype;
// 模块的初始化函数
static int mykobj_init(void)
{
int ret;
// 创建kobject的第一种方法
// 创建并添加了名为"mykobject01"的kobject对象,父kobject为NULL
mykobject01 = kobject_create_and_add("mykobject01", NULL);
// 创建并添加了名为"mykobject02"的kobject对象,父kobject为mykobject01。
mykobject02 = kobject_create_and_add("mykobject02", mykobject01);
// 创建kobject的第二种方法
// 1 使用kzalloc函数分配了一个kobject对象的内存
mykobject03 = kzalloc(sizeof(struct kobject), GFP_KERNEL);
// 2 初始化并添加到内核中,名为"mykobject03"。
ret = kobject_init_and_add(mykobject03, &mytype, NULL, "%s", "mykobject03");
return 0;
}
// 模块退出函数
static void mykobj_exit(void)
{
// 释放了之前创建的kobject对象
kobject_put(mykobject01);
kobject_put(mykobject02);
kobject_put(mykobject03);
}
module_init(mykobj_init); // 指定模块的初始化函数
module_exit(mykobj_exit); // 指定模块的退出函数
MODULE_LICENSE("GPL"); // 模块使用的许可证
MODULE_AUTHOR("fangchen"); // 模块的作者
kobject 创建方法
在Linux内核中,kobject_create_and_add和kobject_init_and_add都是用于创建和注册kobject的API,但它们的用途和实现方式有显著区别。以下是两者的对比分析:
kobject_create_and_add 方法
功能
- 自动分配内存:动态创建一个kobject对象(包括内存分配),并直接将其添加到内核对象层次结构中。
- 简化流程:适用于不需要自定义kobject初始化的场景,由内核自动完成内存管理和基本初始化。
原型
struct kobject *kobject_create_and_add(const char *name, struct kobject *parent);
特点
- 内存管理:内部调用kzalloc分配内存,返回的kobject指针由调用者通过kobject_put()释放。
- 默认初始化:自动设置引用计数(kref_init)、初始化链表、设置默认属性(sysfs_ops)等。
- 典型用途:快速创建简单的kobject,例如在模块中暴露一个顶层目录或子目录。
示例
struct kobject *kobj = kobject_create_and_add("my_kobj", NULL);
if (!kobj) {
// 错误处理
}
// 使用后需释放
kobject_put(kobj);
kobject_init_and_add 方法
功能
- 手动管理内存:对已分配的kobject(通常是嵌入在更大结构体中的成员)进行初始化并注册。
- 灵活控制:允许调用者完全控制kobject的内存来源和部分初始化参数。
原型
int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,
struct kobject *parent, const char *fmt, ...);
特点
- 内存由调用者提供:kobj必须指向已分配的内存(通常是静态变量或嵌入在其他结构体中)。
- 需指定kobj_type:必须提供ktype参数,定义该kobject的行为(如释放函数、属性操作等)。
- 典型用途:需要将kobject嵌入到自定义结构体时(如设备驱动中的私有数据)。
示例
struct my_struct {
struct kobject kobj;
int data;
};
struct my_struct *obj = kzalloc(sizeof(*obj), GFP_KERNEL);
if (!obj) {
// 错误处理
}
// 初始化并注册
int ret = kobject_init_and_add(&obj->kobj, &my_ktype, NULL, "my_kobj");
if (ret) {
kfree(obj);
// 错误处理
}
// 释放时需通过ktype中的release函数处理
kobject_put(&obj->kobj);
关键区别kobject_create_and_add - kobject_init_and_add
| 特性 | kobject_create_and_add | kobject_init_and_add |
|---|---|---|
| 内存分配 | 内部动态分配 | 由调用者预先分配 |
| 初始化控制 | 自动完成基础初始化 | 需手动指定kobj_type等参数 |
| 适用场景 | 独立kobject | kobject嵌入到其他结构体中 |
| 释放方式 | 直接kobject_put() | 通过ktype->release回调释放外层结构 |
| 灵活性 | 较低 | 较高 |
如何选择kobject_create_and_add - kobject_init_and_add
- 优先kobject_init_and_add:当kobject是自定义结构体的一部分时(常见于设备驱动)。
- 使用kobject_create_and_add:当仅需一个独立的kobject且无需复杂控制时(例如创建简单sysfs节点)。
kobject的主要功能
- 引用计数:通过kref实现,确保对象不再使用时被释放
- sysfs表示:每个kobject对应sysfs中的一个目录
- 父子关系:构建设备层次结构
- 对象关联:通过kset组织相关对象
三、kset详解
kset 是一组 kobject 的集合,提供了:
- 包含多个 kobject 的容器
- 在 sysfs 中作为目录出现
- 管理 kobject 的生命周期
kset的创建
kset是kobject的集合,本身也是一个kobject:
struct kset {
struct list_head list; // 包含的kobject链表
spinlock_t list_lock; // 保护链表的锁
struct kobject kobj; // 内嵌的kobject
const struct kset_uevent_ops *uevent_ops; // 热插拔操作集
};
创建kset的示例:
struct kset *my_kset;
my_kset = kset_create_and_add("my_kset", NULL, NULL);
if (!my_kset) {
return -ENOMEM;
}
kset的主要功能
- 集合管理:将相关的kobject组织在一起
- uevent支持:处理热插拔事件
- 引用计数:通过内嵌的kobject实现
- sysfs表示:作为父目录包含成员kobject
创建kobject并加入kset
static struct kobject *mykobj;
static struct kset *mykset;
static int __init my_init(void)
{
// 创建kset
mykset = kset_create_and_add("mykset", NULL, NULL);
if (!mykset)
return -ENOMEM;
// 创建kobject并加入kset
mykobj = kzalloc(sizeof(*mykobj), GFP_KERNEL);
if (!mykobj) {
kset_unregister(mykset);
return -ENOMEM;
}
kobject_init(mykobj, &my_ktype);
mykobj->kset = mykset;
int ret = kobject_add(mykobj, NULL, "mykobj");
if (ret) {
kobject_put(mykobj);
kset_unregister(mykset);
return ret;
}
return 0;
}
四、 kobject与kset的区别-联系-小结
区别
| 特性 | kobject | kset |
|---|---|---|
| 本质 | 基础对象 | kobject的集合 |
| 功能 | 提供基本对象特性 | 管理一组相关kobject |
| sysfs表示 | 对应一个目录 | 对应一个目录,包含成员kobject |
| 主要用途 | 构建设备模型的基础元素 | 组织和管理相关kobject |
| 引用计数 | 直接管理 | 通过内嵌kobject管理 |
联系
- 包含关系:kset内嵌了一个kobject,因此kset也是一种特殊的kobject
- 层次结构:kset可以作为kobject的父对象
- 协同工作:kobject通常属于某个kset,kset管理一组相关的kobject
- 共用机制:都使用ktype定义对象类型行为
小结
Linux设备模型通过kobject和kset构建了一个灵活的层次结构:
- kobject是基础构建块,提供核心功能
- kset组织相关kobject,便于管理和事件处理
- 两者协同工作,形成了设备模型的骨架
- 上层结构(device, driver, bus, class)都基于此构建
理解kobject和kset的关系是掌握Linux设备模型的关键,它们共同实现了设备在sysfs中的表示、引用计数管理、热插拔支持等核心功能。
五、创建kobject 和 keyset 示例
keyobjet 示例及分析
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/configfs.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
// 定义了三个kobject指针变量:mykobject01、mykobject02、mykobject03
struct kobject *mykobject01;
struct kobject *mykobject02;
struct kobject *mykobject03;
// 定义了一个kobj_type结构体变量mytype,用于描述kobject的类型。
struct kobj_type mytype;
// 模块的初始化函数
static int mykobj_init(void)
{
int ret;
// 创建kobject的第一种方法
// 创建并添加了名为"mykobject01"的kobject对象,父kobject为NULL
mykobject01 = kobject_create_and_add("mykobject01", NULL);
// 创建并添加了名为"mykobject02"的kobject对象,父kobject为mykobject01。
mykobject02 = kobject_create_and_add("mykobject02", mykobject01);
// 创建kobject的第二种方法
// 1 使用kzalloc函数分配了一个kobject对象的内存
mykobject03 = kzalloc(sizeof(struct kobject), GFP_KERNEL);
// 2 初始化并添加到内核中,名为"mykobject03"。
ret = kobject_init_and_add(mykobject03, &mytype, NULL, "%s", "mykobject03");
return 0;
}
// 模块退出函数
static void mykobj_exit(void)
{
// 释放了之前创建的kobject对象
kobject_put(mykobject01);
kobject_put(mykobject02);
kobject_put(mykobject03);
}
module_init(mykobj_init); // 指定模块的初始化函数
module_exit(mykobj_exit); // 指定模块的退出函数
MODULE_LICENSE("GPL"); // 模块使用的许可证
MODULE_AUTHOR("fangchen"); // 模块的作者

kset 示例及分析
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/configfs.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
// 定义kobject结构体指针,用于表示第一个自定义内核对象
struct kobject *mykobject01;
// 定义kobject结构体指针,用于表示第二个自定义内核对象
struct kobject *mykobject02;
// 定义kset结构体指针,用于表示自定义内核对象的集合
struct kset *mykset;
// 定义kobj_type结构体,用于定义自定义内核对象的类型
struct kobj_type mytype;
// 模块的初始化函数
static int mykobj_init(void)
{
int ret;
// 创建并添加kset,名称为"mykset",父kobject为NULL,属性为NULL
mykset = kset_create_and_add("mykset", NULL, NULL);
// 为mykobject01分配内存空间,大小为struct kobject的大小,标志为GFP_KERNEL
mykobject01 = kzalloc(sizeof(struct kobject), GFP_KERNEL);
// 将mykset设置为mykobject01的kset属性
mykobject01->kset = mykset;
// 初始化并添加mykobject01,类型为mytype,父kobject为NULL,格式化字符串为"mykobject01"
ret = kobject_init_and_add(mykobject01, &mytype, NULL, "%s", "mykobject01");
// 为mykobject02分配内存空间,大小为struct kobject的大小,标志为GFP_KERNEL
mykobject02 = kzalloc(sizeof(struct kobject), GFP_KERNEL);
// 将mykset设置为mykobject02的kset属性
mykobject02->kset = mykset;
// 初始化并添加mykobject02,类型为mytype,父kobject为NULL,格式化字符串为"mykobject02"
ret = kobject_init_and_add(mykobject02, &mytype, NULL, "%s", "mykobject02");
return 0;
}
// 模块退出函数
static void mykobj_exit(void)
{
// 释放mykobject01的引用计数
kobject_put(mykobject01);
// 释放mykobject02的引用计数
kobject_put(mykobject02);
}
module_init(mykobj_init); // 指定模块的初始化函数
module_exit(mykobj_exit); // 指定模块的退出函数
MODULE_LICENSE("GPL"); // 模块使用的许可证
MODULE_AUTHOR("fangchen"); // 模块的作者

API 小结
| 函数 | 内存分配方式 | 初始化方式 | sysfs添加 | 适用场景 |
|---|---|---|---|---|
| kobject_create_and_add | 内部分配 | 内部完成 | 是 | 快速创建独立kobject |
| kobject_init_and_add | 外部分配 | 需指定ktype | 是 | 自定义分配或静态kobject |
| kset_create_and_add | 内部分配 | 内部完成 | 是 | 创建kobject集合 |
函数 kobject_put
函数原型
void kobject_put(struct kobject *kobj);
功能:
- 减少kobject的引用计数,当计数为0时释放资源
函数 kzalloc
函数原型
void *kzalloc(size_t size, gfp_t flags);
功能
- 内核空间的内存分配函数,分配后自动清零
实际用法举例:

总结
了解设备模型基础知识
970

被折叠的 条评论
为什么被折叠?



