linux 内核结构基础
linux 内核对象基础
- 1.linux 的 kobj(struct kobject) 类
- 2.linux 中的 kset
- 3. linux 下的 ktype (kobj_type 类)
- 4. kobj 的使用理解
1.linux 的 kobj(struct kobject) 类
kobj
是 linux 下的高级抽象类定义,用于派生所有其余的类定义,比如设备类定义struct device
.
struct kobject
定义在 include/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;
};
kobj
成员说明:
成员 | 描述 |
---|---|
name | kobj 名字,会显示在 /sys/ 目录下 |
entry | 双向链接节点,用于被链入所属的 kset |
parent | 指向 parent kobj, 在 /sys/ 中会以目录的形式呈现 |
kset | 当前 kobj 所属的 kset,kset 是一个特殊的 kobj, 用来集合相似的 kobj,表示父目录 |
ktype | 每一个 kobj 都必须有一个 ktype,用于访问该内核文件特有的属性,用于生成目录下的文件,以及实现文件读写驱动 |
sd | 当前 kobj 在 sysfs 中的目录入口,用于表示文件对象节点对象,kernfs_node 结构体中的 priv 成员会设置为 kobject 对象。 |
kref | kobj 引用计数 |
state_initialized | 初始化状态 |
state_in_sysfs | 当前 kobj 是否已在 /sys/ 中呈现 |
state_add_uevent_sent | 记录是否已经向用户空间发送 uevent |
state_remove_uevent_sent | 记录移除节点时,是否向用户空间发送 remove uevent |
uevent_suppress | 是否忽略所有的用户空间事件上报,即事件通知 |
2.linux 中的 kset
kset
用于集合一系列相似的 kobj
(类比某根目录?), 原型如下:
struct kset {
struct list_head list;
spinlock_t list_lock;
struct kobject kobj;
const struct kset_uevent_ops *uevent_ops;
} __randomize_layout;
kset
是一个特殊的 kobj
,成员说明如下:
成员 | 描述 |
---|---|
list | 双向链表头,用于链接管理 kobj |
list_lock | 自旋锁,防止接口竞争 |
kobj | kset 是一个特殊的 kobj, kset 也会在 ‘/sys/’ 目录下出现 |
uevent_ops | 事件操作? |
比如 /sys/bus/ 存在多个总线,bus 就属于一个 kset?
比如 /sys/class/ 存在多个 class,class 也属于一个 kset?
(似乎是的)
那么猜测: kset 用于表示 /sys/ 目录下的一级目录。
3. linux 下的 ktype (kobj_type 类)
ktype
用于实现 kobj
的一些特殊操作,比如释放 kobj
空间。
原型如下:
struct kobj_type {
void (*release)(struct kobject *kobj);
const struct sysfs_ops *sysfs_ops;
struct attribute **default_attrs; /* use default_groups instead */
const struct attribute_group **default_groups;
const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
const void *(*namespace)(struct kobject *kobj);
void (*get_ownership)(struct kobject *kobj, kuid_t *uid, kgid_t *gid);
};
最重要的 release
函数指针,用于释放 kobj
空间。
sysfs_ops 用于访问当前 kobj 的属性文件。
/**
* 作为 kobj_type 的成员,用于访问该内核对象下的属性文件的通用读写操作,而实际的属性读写函数,由驱动实现。
* 一般用于内核层,比如内核总线、class 的属性访问方法,会调用实际总线、class 实现的属性访问方法
*/
struct sysfs_ops {
ssize_t (*show)(struct kobject *, struct attribute *, char *);
ssize_t (*store)(struct kobject *, struct attribute *, const char *, size_t);
};
struct attribute **default_attrs
为属性指针数组,该数组保存一系列的属性指针。
/* 内核文件的属性,一个属性对应一个文件 */
/**
* 这是通用的属性对象,使用时会对该对象进行封装,比如总线属性,设备属性,一般会根据实际情况封装属性文件的读写方法。
*/
struct attribute {
const char *name;
umode_t mode;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
bool ignore_lockdep:1;
struct lock_class_key *key;
struct lock_class_key skey;
#endif
};
还存在一个 struct attribute_group
类,用于存储一组属性,但用法没有研究透。
struct attribute_group {
const char *name;
umode_t (*is_visible)(struct kobject *,
struct attribute *, int);
umode_t (*is_bin_visible)(struct kobject *,
struct bin_attribute *, int);
struct attribute **attrs;
struct bin_attribute **bin_attrs;
};
代码分析:根据 sysfs_create_groups()
函数实现,如果 struct attribute_group
name 成员实现,那么会在指定的 kobj 对象下另建一个 group 目录,并在 group 目录下创建所有的可见属性文件。
4. kobj 的使用理解
kobj
大多时候是嵌入到高级数据结构中,比如设备定义 struct device
中。
这些高级数据结构一般是动态分配的,当内嵌的 kobj 的引用计数 kref
减为 0 时,需要释放高级数据结构所占内存,以及对应的 kobj 内存。
此时,需要使用 kobj 中 ktype
定义的 release
函数指针。
所以,每一个包含 kobj 的高级数据结构,都必须实现所属的 ktype。
基于面向对象编程的思想,系统用户看到的只有 kobj,而不会看到驱动相关的数据结构。
以访问 /sys/
下的文件举例, 基于一切皆文件的思想,linux 用户向/sys
访问 kobj 的流程如下:
应用层 | linux应用层 open\read\write\close 访问文件 |
---|---|
VFS/kernfs | linux VFS 文件系统根据文件所属的实际文件系统,调用 sysfs 文件系统的 open\read\write\close |
sysfs | sysfs 文件系统的实际驱动根据获取 kobj 对象,并调用 kobj 提供的属性(文件)访问接口 |
kobj | kobj 调用驱动层提供的属性访问接口。 |
驱动 | 驱动对私有属性的实现。 |
以访问字符设备 /dev/tty0
举例, 基于一切皆文件的思想,linux 用户向/dev/tty0
访问 kobj 的流程如下:
应用层 | linux应用层 open\read\write\close 访问文件 |
---|---|
VFS/kernfs | linux VFS 文件系统根据文件所属的实际文件系统,获取 knode, 并调用设备文件系统的 open\read\write\close |
设备文件系统? | 设备文件系统根据 knode 结构获取 kobj 结构,并转换为 cdev 结构,然后直接调用驱动注册的 open/read/write/close |
驱动 | 驱动对open\read\write\close的实现。 |
kobj 是系统内核能访问的最底层数据结构,其余结构都是对 kobj 的再封装。
Linux应用层访问驱动自定义class属性流程如下:
应用层到系统内核层:
-
应用层使用 open/read/write/close 访问某一文件系统的文件, 系统调用;
-
Kernfs 层根据文件系统类型调用文件系统的驱动,并获取当前文件的 knode 结构,文件的 knode 结构是对 kobj 的再封装(knode 对象与 kobj 对象相互引用);
-
Sysfs 层会通过 knode 结构获取 kobj 结构,并调用文件(属性)的读/写;
-
Kobj 层会调用属性访问驱动将对应属性传递到 class 层;
驱动层:
驱动层会自定义 class 属性,并定义属性访问函数;实现驱动变量的控制
-
Class 层接收内核层对某一属性的读写访问,实现由应用层来控制驱动变量的值,控制驱动行为。
-
驱动根据属性变量值,做出不同的行为。
注意:
- 每一个驱动自定义 class 属性都会生成一个文件,通过调用 class_create_file()。
- class 层是对 kobj 的封装层,源码比较容易理解.