【linux kernel】 一文总结linux内核中的kobject、kset和ktype
'# 【linux kernel】 一文总结linux内核中的kobject、kset和ktype
一、背景与问题
在Linux内核开发中,设备驱动和子系统需要管理大量动态创建的内核对象。传统的struct device等结构体难以满足这种动态性需求,于是内核引入了kobject体系作为通用对象管理框架。该体系包含三个核心组件:
- kobject:基本对象模型,提供统一的引用计数和属性管理
- kset:对象集合,提供分类管理能力
- ktype:类型管理,定义对象的属性和操作
这些组件共同构建了内核的设备模型(device model),是sysfs文件系统、热插拔支持、设备属性管理等特性的基础。
二、基本原理
1. kobject核心结构
struct kobject {
const char *name;
struct list_head entry;
struct kobject *parent;
struct kset *kset;
struct ktype *ktype;
struct bin_attribute *bin_attrs;
struct attribute *attrs;
struct kobj_attribute *default_attrs;
struct kref kref;
unsigned int state;
void *ptr;
};name:对象名称parent:父对象指针kset:所属集合ktype:类型描述kref:引用计数器state:状态标志(如KOBJ_STATE_IN_TREE)ptr:指向具体对象的指针(如struct device)
2. kset结构
struct kset {
struct list_head list;
struct kobject kobj;
struct kset *parent;
const struct kset_ops *ops;
};kobj:kset自身的kobjectparent:父ksetops:操作函数集合(如kset_add、kset_del)
3. ktype结构
struct ktype {
const char *name;
struct attribute_group *default_groups;
const struct kobj_attribute *attrs;
const struct bin_attribute *bin_attrs;
const struct kobj_attribute *default_attrs;
const struct kobj_attribute *sysfs_ops;
const struct kobj_attribute *sysfs_ops2;
const struct kobj_attribute *sysfs_ops3;
const struct kobj_attribute *sysfs_ops4;
const struct kobj_attribute *sysfs_ops5;
};name:类型名称default_groups:默认属性组attrs/bin_attrs:属性描述
三、环境准备
#include <linux/kobject.h>
#include <linux/kset.h>
#include <linux/ktype.h>
#include <linux/module.h>
#include <linux/kernel.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");需要编译为内核模块,使用make构建:
obj-m += kobject_demo.o四、核心实现
1. 创建kset
static struct kset *my_kset;
static int __init kobject_init(void) {
my_kset = kset_create("my_kset", &kset_default_ops);
if (!my_kset)
return -ENOMEM;
// 注册到内核
kobject_put(&my_kset->kobj);
return 0;
}关键点:
- 使用
kset_create创建kset kset_default_ops是默认操作集合kobject_put将kset注册到sysfs
2. 创建kobject
static struct kobject *my_kobj;
static int __init kobject_init(void) {
my_kobj = kobject_create_and_add("my_kobj", my_kset->kobj);
if (!my_kobj)
return -ENOMEM;
// 添加属性
static struct attribute *attrs[] = {
&sysfs_bin_attr("data", 0444, NULL, NULL),
NULL
};
kobject_add_attrs(my_kobj, attrs);
return 0;
}关键点:
kobject_create_and_add创建并注册kobjectsysfs_bin_attr创建二进制属性kobject_add_attrs将属性绑定到kobject
3. 实现ktype
static struct ktype my_ktype = {
.name = "my_ktype",
.default_groups = NULL,
.attrs = NULL,
.bin_attrs = NULL,
};
static int __init kobject_init(void) {
// 注册ktype
ktype_register(my_kobj, &my_ktype);
return 0;
}关键点:
ktype_register将ktype绑定到kobject- 需要确保kobject的
ktype指针正确指向
五、完整案例:实现一个简单的sysfs接口
#include <linux/kobject.h>
#include <linux/kset.h>
#include <linux/ktype.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sysfs.h>
#include <linux/fs.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
static struct kset *my_kset;
static struct kobject *my_kobj;
static struct ktype my_ktype;
// 属性操作函数
static ssize_t show_data(struct kobject *kobj, struct kobj_attribute *attr, char *buf) {
return sprintf(buf, "Sample data\n");
}
static ssize_t store_data(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) {
printk(KERN_INFO "Received: %s\n", buf);
return count;
}
// 属性描述
static struct kobj_attribute my_attr = {
.attr = {
.name = "data",
.mode = 0644
},
.show = show_data,
.store = store_data
};
static struct attribute *attrs[] = {
&my_attr.attr,
NULL
};
static struct attribute_group my_attr_group = {
.attrs = attrs
};
// 初始化函数
static int __init kobject_init(void) {
// 创建kset
my_kset = kset_create("my_kset", &kset_default_ops);
if (!my_kset)
return -ENOMEM;
// 创建kobject
my_kobj = kobject_create_and_add("my_kobj", my_kset->kobj);
if (!my_kobj)
return -ENOMEM;
// 注册属性组
kobject_add_group(my_kobj, &my_attr_group);
// 注册ktype
my_ktype.name = "my_ktype";
ktype_register(my_kobj, &my_ktype);
return 0;
}
// 模块卸载函数
static void __exit kobject_exit(void) {
kobject_put(my_kobj);
kset_unregister(my_kset);
}
module_init(kobject_init);
module_exit(kobject_exit);关键点:
- 使用
kobj_attribute定义属性操作 kobject_add_group将属性组绑定到kobject- 模块卸载时需要正确释放资源
六、源码解析
1. kobject的引用计数
void kobject_get(struct kobject *kobj) {
kref_get(&kobj->kref);
}- 引用计数器机制确保对象不会被过早释放
- 当引用计数为0时,调用
kobject_put会触发销毁流程
2. kset的注册流程
int kset_register(struct kset *kset) {
kobject_put(&kset->kobj);
return 0;
}- 确保kset的kobject被正确注册
- 避免重复注册导致的资源泄漏
3. ktype的绑定过程
void ktype_register(struct kobject *kobj, struct ktype *ktype) {
kobj->ktype = ktype;
}- 将ktype绑定到kobject
- 实现属性的动态管理
七、进阶使用
1. 多级kset组织
struct kset *parent_kset;
struct kset *child_kset;
child_kset = kset_create("child", &kset_default_ops);
kset_add(child_kset, parent_kset);- 用于构建层次化的设备树
- 适用于USB、PCI等复杂总线架构
2. 事件通知系统
struct kset *my_kset;
struct kobject *my_kobj;
// 注册通知
kobject_add_notifier(my_kobj, KNOTIFY_CREATE | KNOTIFY_REMOVE, my_notifier);- 实现对象的动态监控
- 用于设备热插拔等场景
3. 内存管理优化
struct my_obj {
struct kobject kobj;
char data[1024];
};
static int my_init(struct kobject *kobj, const char *name) {
struct my_obj *obj = to_my_obj(kobj);
strcpy(obj->data, name);
return 0;
}- 在kobject结构中嵌入自定义数据
- 优化内存使用,减少内存碎片
八、性能与工程实践
1. 性能优化
- 减少锁竞争:使用
kref_get/kref_put代替全局锁 - 内存池管理:使用
kmem_cache预分配对象 - 异步处理:将耗时操作放入工作队列
2. 异常处理
void kobject_put(struct kobject *kobj) {
if (kobj && !kobj->kref.count)
kobject_put(kobj->parent);
}- 确保父对象不会被过早释放
- 避免悬空指针
3. 安全风险
- 权限控制:通过
mode位控制属性访问权限 - 数据验证:在
store回调中进行输入验证 - 内存安全:避免
kmalloc的内存泄漏
九、常见问题与踩坑
1. 常见错误
错误示例:
kobject_create_and_add("my_kobj", NULL);问题分析:
- 父对象为NULL时,kobject会注册到根kset
- 可能导致sysfs路径混乱
解决方案:
kobject_create_and_add("my_kobj", my_kset->kobj);2. 内存泄漏
错误示例:
struct kobject *kobj = kobject_create_and_add("my_kobj", NULL);问题分析:
- 没有显式调用
kobject_put - 模块卸载时未释放资源
解决方案:
kobject_put(kobj);3. 属性未显示
错误示例:
kobject_add_attrs(kobj, NULL);问题分析:
- 没有添加任何属性
- sysfs中不会显示任何内容
解决方案:
static struct attribute *attrs[] = {
&sysfs_bin_attr("data", 0444, NULL, NULL),
NULL
};
kobject_add_attrs(kobj, attrs);十、最佳实践
- 统一管理:所有设备对象应通过kobject/kset进行统一管理
- 分层设计:使用多级kset构建层次化设备树
- 属性分离:将业务属性与系统属性分离管理
- 生命周期管理:确保引用计数正确,避免资源泄漏
- 安全控制:严格控制属性的访问权限
- 性能优化:避免不必要的锁和内存分配
十一、总结
kobject/kset/ktype体系是Linux内核设备模型的核心,提供了统一的内核对象管理机制。通过理解其原理和实现细节,开发者可以构建更健壮的设备驱动和子系统。在实际开发中需要注意:
- 适用场景:适用于需要动态管理大量对象的场景(如设备驱动、系统子系统)
- 不适用场景:轻量级对象管理、对性能要求极高的场景
通过合理使用这些机制,可以实现更高效的资源管理、更清晰的系统架构,以及更完善的设备接口。在开发过程中要特别注意引用计数、内存管理、权限控制等关键点,避免常见的陷阱和错误。
评论已关闭