01、设备模型基本框架
一、koject的重点知识点
1.1、结构体定义:struct kobject
kobject
是什么:这是一个内核中的特殊结构体(数据容器),用于管理设备、驱动或其他内核对象的信息。- 作用:像一个“通用盒子”,可以挂载属性(如设备名称、状态等),并将其暴露给用户空间(如通过文件系统查看)。
1.2、kobject的作用
核心功能:
- 统一管理对象:把内核中的设备、驱动等抽象为统一的“对象”,方便内核统一操作。
- 暴露信息给用户:将对象的属性(如温度、状态等)通过文件形式显示在文件系统中,用户可直接读写这些文件。
- 组织层级关系:通过父子关系将相关对象分组,形成树状结构(类似文件目录)。
1.3、对应sysfs下的目录
sysfs是什么: 内核的特殊文件系统,专门用来展示内核对象的信息(如设备、驱动属性)。
kobject与目录的关系: 每个
- 若某个
kobject
被添加到/sys/devices/
,则会生成类似/sys/devices/my_kobj
的目录。 - 该目录下的文件(如
name
、status
)存储了对象的具体信息。
- 若某个
1.4、 kobject的初始化和添加流程
步骤分解:
- 定义结构体:在代码中声明一个
struct kobject
变量(例如:struct kobject my_kobj;
)。 - 初始化:用
kobject_init()
或kobject_init_and_add()
初始化该结构体,设置名称等基本信息。 - 指定父目录(可选):如果需要将对象归类到某个父目录下,需指定父
kobject
。 - 添加到内核:调用
kobject_add()
将对象注册到内核,此时sysfs会自动生成对应的目录。 - 添加属性(可选):通过
kobject_create_file()
为对象创建属性文件(如temp
、enable
)。
- 定义结构体:在代码中声明一个
结果: 用户可在/sys
目录下看到生成的目录和文件,例如:
/sys/devices/my_kobj/name # 存储对象名称
/sys/devices/my_kobj/status # 存储运行状态
2
1.5、总结
- kobject是内核管理对象的通用工具,通过它可将内核信息以文件形式暴露在
sysfs
中。 - 流程:定义结构体 → 初始化 → 指定位置 → 添加到内核 → 自动生成sysfs目录和文件。
这样描述是否更清晰?如果有疑问可以继续提问!
二、kobject 和 kset 基本概念
kobject 和 kset 是 Linux 内核中用于管理内核对象的基本概念。(基类)
2.1、kobject (基石)
kobject是Linux内核中设计的一个通用结构,用来统一表示内核里的各种实体(比如设备、驱动等)。它是一个包含对象基本信息和操作方法的结构体,通过统一的方式来进行管理、操作和控制。这个结构体定义在内核源码的linux/kobject.h文件中。
struct kobject {
const char *name;
struct list_head entry;
struct kobject *parent;
struct kset *kset;
struct kobj_type *ktype;
struct kernfs_node *sd; /* sysfs directory entry */
struct kref kref; //引用计数
#ifdef CONFIG_DEBUG_KOBJECT_RELEASE
struct delayed_work release;
#endif
unsigned int state_initialized:1;
unsigned int state_in_sysfs:1;
unsigned int state_add_uevent_sent:1;
unsigned int state_remove_uevent_sent:1;
unsigned int uevent_suppress:1;
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
21
22
这个结构体用来管理内核中的对象,主要字段含义如下:
名称(name) 一个字符串,记录对象的名字。这个名称会用来在系统文件夹
/sys
里创建对应的目录。连接项(entry) 一个链表项,用来把当前对象链接到父对象的子列表中,形成层级结构。
父对象(parent) 指向当前对象的父对象,用来表示对象之间的层级关系。
所属集合(kset) 指向包含该对象的集合(kset),帮助组织和管理大量同类对象。
类型定义(ktype) 指向描述对象类型的结构体,定义了该对象的功能和操作方式(比如如何创建/删除)。
系统文件节点(sd) 指向
sysfs
文件系统中的对应目录项,用于管理和操作内核对象的系统文件。引用计数(kref) 跟踪有多少地方正在使用该对象,确保在完全不用时才释放资源。
状态标记(flags) 一组状态标记位,记录对象当前情况,例如:
- 是否已初始化
- 是否已注册到系统文件
- 是否已发送设备事件(uevent)等
这些字段共同协作,帮助内核高效地管理对象的生命周期、层级关系和系统文件交互。
2.2、kobj_type(行为)
kobj_type
结构体定义了内核对象(kobject)的行为规则。每个使用kobject的自定义结构体都需要一个对应的kobj_type,kobj_type就像kobject的"操作说明书",告诉内核:
- 对象释放时要做哪些清理工作(release)
- 如何处理用户对sysfs文件的读写请求(sysfs_ops)
- 默认要创建哪些属性文件(default_attrs)
这种设计让不同类型的内核对象可以共享相同的操作逻辑,同时又能保持各自特有的行为。
struct kobj_type {
void (*release)(struct kobject *); // 1. 对象释放时的清理函数
const struct sysfs_ops sysfs_ops; // 2. sysfs文件操作的回调函数
struct attribute **default_attrs; // 3. 默认属性列表
};
2
3
4
5
详细说明:
- release 函数 当kobject不再被使用时(通过kobject_put()触发),系统会自动调用这个函数。 → 必须在这里释放对象占用的内存资源。 → 可以用
container_of
宏获取原始对象指针(类似"向上转型")。 - sysfs_ops 操作集合 定义了访问sysfs属性时的具体操作:
struct sysfs_ops {
ssize_t (*show)(..., char *buf); // 读取属性值时调用(类似文件读操作)
ssize_t (*store)(..., const char *buf); // 写入属性值时调用(类似文件写操作)
};
2
3
4
- default_attrs 默认属性 指向一组预定义的属性列表,每个使用该kobj_type的kobject都会自动获得这些属性。 → 类似于"默认配置",所有实例共享这些属性。
2.3、kset (集合)
kset是Linux内核中用来管理一组相关kobject的容器,就像文件夹用来组织文件一样。它的结构定义如下:
目录:linux/kobject.h
/**
* struct kset - a set of kobjects of a specific type, belonging to a specific subsystem.
*
* A kset defines a group of kobjects. They can be individually
* different "types" but overall these kobjects all want to be grouped
* together and operated on in the same manner. ksets are used to
* define the attribute callbacks and other common events that happen to
* a kobject.
*
* @list: the list of all kobjects for this kset
* @list_lock: a lock for iterating over the kobjects
* @kobj: the embedded kobject for this kset (recursion, isn't it fun...)
* @uevent_ops: the set of uevent operations for this kset. These are
* called whenever a kobject has something happen to it so that the kset
* can add new environment variables, or filter out the uevents if so
* desired.
*/
struct kset {
struct list_head list;
spinlock_t list_lock;
struct kobject kobj;
const struct kset_uevent_ops *uevent_ops;
} __randomize_layout;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
简单解释:
- 它本质是一个kobject的扩展,自带一个kobject成员,所以在/sys里会生成对应的目录
- 通过"链接节点"把自己加入系统全局的kset列表,方便统一管理
- 使用"自旋锁"来保证多核CPU访问时的数据安全
- 可以自定义设备事件处理方式,比如当成员kobject增减时发送通知
核心作用:把多个相关设备对象(kobject)组织成组,方便统一管理和在/sys文件系统中形成清晰的目录结构。
2.4、kset 和 kobject 的关系
在Linux内核中,kset和kobject是两个紧密关联的概念,它们的关系可以这样理解:
简单来说:
- kset是kobject的"分类容器" kset可以看作是kobject的特殊类型,它像一个文件夹一样,能包含多个kobject。通过kset,内核可以把相关联的kobject组织成一组,方便统一管理。
- 每个kobject必须属于一个kset 每个kobject对象都像文件一样,必须存放在某个"文件夹"(kset)里。kobject内部会记录自己所属kset的位置(通过一个指针)。
具体功能:
- kset管理kobject的集合 通过kset,内核可以直接操作它包含的所有kobject,比如批量添加/删除、查找特定对象,或者统一处理事件(比如设备状态变化通知)。
- 结构关系是严格的 一个kset可以包含多个kobject,但一个kobject只能属于一个kset,不能同时存在于多个分类中。
总结成四点:
- kset是kobject的容器,用来分组管理多个kobject
- 每个kobject必须且只能属于一个kset
- kset提供批量操作功能,方便统一管理同类对象
- 这种设计让内核能高效组织和操作各种设备、驱动等对象
就像文件系统里文件(kobject)必须放在文件夹(kset)中一样,这种结构让内核管理复杂对象时更清晰有序。
三、常用的相关API接口
API接口汇总在include/linux/kobject.h
extern void kobject_init(struct kobject *kobj, struct kobj_type *ktype);
extern __printf(3, 4) __must_check
int kobject_add(struct kobject *kobj, struct kobject *parent,
const char *fmt, ...);
extern __printf(4, 5) __must_check
int kobject_init_and_add(struct kobject *kobj,
struct kobj_type *ktype, struct kobject *parent,
const char *fmt, ...);
extern void kobject_del(struct kobject *kobj);
extern struct kobject * __must_check kobject_create(void);
extern struct kobject * __must_check kobject_create_and_add(const char *name,
struct kobject *parent);
extern int __must_check kobject_rename(struct kobject *, const char *new_name);
extern int __must_check kobject_move(struct kobject *, struct kobject *);
extern struct kobject *kobject_get(struct kobject *kobj);
extern struct kobject * __must_check kobject_get_unless_zero(
struct kobject *kobj);
extern void kobject_put(struct kobject *kobj);
extern const void *kobject_namespace(struct kobject *kobj);
extern void kobject_get_ownership(struct kobject *kobj,
kuid_t *uid, kgid_t *gid);
extern char *kobject_get_path(struct kobject *kobj, gfp_t flag);
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
对上面重要接口进行详细梳理:
3.1、kobject 接口
每个内核设备都会使用kobject对象。在设备被系统使用前,必须先用kobject_create()函数创建kobject对象。这个未初始化的kobject对象需要通过kobj_init()函数进行初始化。在调用kobj_init()时,需要提供这个kobject指针和对应的kobj_type类型指针作为参数。
struct kobject *kobject_create(void)
void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
2
将kobject绑定到系统的代码如下:
static struct kobject *mykobj;
mykobj = kobject_create();
if (mykobj) {
kobject_init(mykobj, &mytype);
if (kobject_add(mykobj, NULL, "%s", "hello")) {
err = -1;
printk("ldm: kobject_add() failed\n");
kobject_put(mykobj);
mykobj = NULL;
}
err = 0;
}
2
3
4
5
6
7
8
9
10
11
12
以下是更直白的改写版本:
要同时完成kobject对象的创建和注册,可以直接使用kobject_create_and_add函数。这个函数会自动执行两个步骤:
- 分配内存并初始化kobject结构(等同于kobject_create)
- 将对象添加到sysfs目录(等同于kobject_add)
案例:
static struct kobject * class_kobj = NULL;
static struct kobject * devices_kobj = NULL;
/* 创建/sys/class */
class_kobj = kobject_create_and_add("class", NULL);
if (!class_kobj) {
return -ENOMEM;
}
/* 创建 /sys/devices */
devices_kobj = kobject_create_and_add("devices", NULL);
if (!devices_kobj) {
return -ENOMEM;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
3.2、kset 接口
每个注册的(添加到系统中的)kset对应于sysfs目录。可以使用kset_create_and_add()函数创建和添加kset,使用kset_unregister()函数将其删除:
struct kset * kset_create_and_add(const char *name,
const struct kset_uevent_ops *u,
struct kobject *parent_kobj);
void kset_unregister (struct kset * k);
2
3
4
将kobject添加到该集合非常简单,只需将其kset字段指定为正确的kset即可:
static struct kobject foo_kobj, bar_kobj;
example_kset = kset_create_and_add("kset_example", NULL, kernel_kobj);
/*
* 因为有这个kobject的kset
* 所以需要在调用kobject core之前设置它
*/
foo_kobj.kset = example_kset;
bar_kobj.kset = example_kset;
retval = kobject_init_and_add(&foo_kobj, &foo_ktype,
NULL, "foo_name");
retval = kobject_init_and_add(&bar_kobj, &bar_ktype,
NULL, "bar_name");
2
3
4
5
6
7
8
9
10
11
12
13