文章目录
- 什么是设备模型?
 - 设备模型的主要组成部分
 - 设备模型的关键功能
 - 设备模型的实现结构
 - 设备模型的重要性
 
- kset和 kobject介绍
 - 1. kobject
 - 2. kset
 - 3. kobject 和 kset 的关系
 - 4. 应用场景
 
- kobject中parent概念
 - 1. parent 字段的作用
 - 2. parent 字段的使用示例
 - 3. sysfs 中的反映
 - 4. 实际场景中的应用
 
- 创建kobject 实验
 - 示例代码
 - `kobject_create_and_add`函数解析
 - `kobject_init_and_add`解析
 - 示例效果
 
- 创建kset 实验
 - 示例代码
 - 参数解析
 - 示例效果
 
什么是设备模型?
设备模型(Device Model)是 Linux 内核中的一个抽象层,用于统一管理和组织系统中的各种硬件设备及其驱动程序。它为设备、驱动、总线和电源管理等提供了一个统一的接口和结构,使得内核能够更加高效和一致地管理系统中的硬件资源。
设备模型的主要组成部分
-  
设备(Device)
- 表示系统中的一个硬件设备,比如一个硬盘驱动器、网络适配器、USB 设备等。每个设备在内核中通常用一个 
struct device结构体来表示。设备与驱动程序、总线都有关系,通常设备会挂载在某个总线上。 
 - 表示系统中的一个硬件设备,比如一个硬盘驱动器、网络适配器、USB 设备等。每个设备在内核中通常用一个 
 -  
驱动(Driver)
- 表示控制设备的程序代码。每个驱动程序都与特定类型的设备关联,驱动程序通过特定的总线接口与设备进行交互。驱动程序在内核中通常用 
struct device_driver结构体来表示。 
 - 表示控制设备的程序代码。每个驱动程序都与特定类型的设备关联,驱动程序通过特定的总线接口与设备进行交互。驱动程序在内核中通常用 
 -  
总线(Bus)
- 设备和驱动程序之间的通信通道。总线将设备与相应的驱动程序连接起来,内核通过总线来匹配设备和驱动程序。总线在内核中用 
struct bus_type结构体来表示。 
 - 设备和驱动程序之间的通信通道。总线将设备与相应的驱动程序连接起来,内核通过总线来匹配设备和驱动程序。总线在内核中用 
 -  
类(Class)
- 表示一组具有相似特性的设备,这些设备可能分布在不同的总线上。类为用户空间提供了一种查看和管理设备的方式。每个类在内核中用 
struct class结构体来表示。 
 - 表示一组具有相似特性的设备,这些设备可能分布在不同的总线上。类为用户空间提供了一种查看和管理设备的方式。每个类在内核中用 
 -  
电源管理(Power Management)
- 设备模型还负责管理设备的电源状态,包括休眠、唤醒等功能,以实现系统的节能和性能优化。
 
 
设备模型的关键功能
-  
设备与驱动的自动匹配:
- 内核设备模型负责将设备和驱动程序匹配起来,即找到合适的驱动程序并将其绑定到设备上。这是通过总线、设备和驱动程序之间的关系来实现的。
 
 -  
设备的层次结构管理:
- 设备模型允许将设备组织成层次结构。例如,一个 PCI 总线可以包含多个设备,这些设备又可以有自己的子设备。内核通过设备模型来管理这些设备的父子关系。
 
 -  
sysfs 文件系统:
- 设备模型与 sysfs 紧密结合,所有的设备、驱动、总线等信息都可以通过 sysfs 文件系统导出到用户空间。sysfs 是用户查看和管理系统硬件的关键接口。
 
 -  
统一的电源管理:
- 设备模型提供了统一的电源管理接口,使得内核可以在系统进入不同电源状态(如挂起、休眠)时,对所有设备进行相应的处理。
 
 
设备模型的实现结构
在 Linux 内核中,设备模型主要通过以下几种核心结构体来实现:
struct device:表示具体的设备。struct device_driver:表示设备驱动程序。struct bus_type:表示设备总线类型。struct class:表示设备的类别。struct kobject:基础对象,用于实现对象的层次结构管理。
设备模型的重要性
设备模型使得 Linux 内核能够以一种模块化和可扩展的方式来管理硬件设备。它提供了抽象接口,使得内核和驱动程序开发者可以更方便地实现设备的管理、控制和交互,而不必关注底层的硬件细节。设备模型的引入极大地简化了 Linux 内核中的设备管理逻辑,并提高了系统的可维护性和扩展性。
kset和 kobject介绍
在Linux内核中,设备模型框架主要通过kobject和kset来组织和管理系统中的各种设备和子系统。这些抽象提供了一种统一的方式来表示内核对象,支持系统中设备和内核组件的层次化组织。以下是kobject和kset的详细介绍:
1. kobject
kobject 是 Linux 内核中表示一个对象的基础结构体,几乎所有的内核对象都可以用 kobject 来表示。kobject 提供了内核对象的基本属性和功能,包括:
- 引用计数:
kobject通过引用计数来管理对象的生命周期,防止对象在未释放前被删除。 - 名字和路径:每个 
kobject都有一个唯一的名字,并可以在内核的 sysfs 文件系统中显示。 - 关联的 kset:
kobject可以被添加到一个kset中,这样就能将kobject组织成一个集合。 - 回调函数:
kobject允许在其被释放时执行特定的回调函数。 
struct kobject {
    const char        *name;
    struct list_head    entry;
    struct kobject        *parent;
    struct kset        *kset;
    struct kobj_type    *ktype;
    struct sysfs_dirent    *sd;
    struct kref        kref;
    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;
};
 
2. kset
kset 是 kobject 的集合,表示一组相关联的 kobject。kset 提供了一种将相关对象组织在一起的机制,这些对象通常共享相同的父对象,并具有相似的操作。
- kset 的结构:
kset本质上是一个包含多个kobject的容器,并且还可以定义一些与这些对象相关的操作。 - 管理机制:
kset管理kobject的创建和销毁,还能够在集合中添加或移除kobject。 
struct kset {
    struct list_head list;
    spinlock_t        list_lock;
    struct kobject    kobj;
    const struct kset_uevent_ops *uevent_ops;
};
 
3. kobject 和 kset 的关系
kobject 通常被添加到一个 kset 中以便更好地组织管理。kset 本身也是一个 kobject,因此 kset 可以嵌套,也就是说一个 kset 可以包含其他 kset。通过这种方式,内核能够建立起设备、驱动和子系统的层次结构。
4. 应用场景
- 设备和驱动模型:
kobject和kset在 Linux 设备模型中被广泛使用,用于组织设备(例如 PCI 设备、USB 设备)和驱动程序,并在 sysfs 文件系统中展示它们的关系。 - sysfs 目录结构:
kobject和kset是 sysfs 文件系统的基础,内核中的许多对象(如设备、驱动程序、子系统)都会在 sysfs 中以文件或目录的形式表示,而这些文件和目录背后就是kobject和kset的实现。 
kobject中parent概念
kobject 结构体中的 parent 字段表示该 kobject 的父对象。这一字段的作用是帮助构建和维护内核对象的层次结构,使得不同的内核对象能够以树形结构组织起来。这种层次结构有助于理清内核对象之间的关系,并在文件系统(如 sysfs)中反映这些关系。
1. parent 字段的作用
-  
层次结构:
parent字段将当前kobject与它的父对象连接起来,形成一个层次化的结构。通过这种结构,内核对象可以形成类似树的组织形式,其中根节点是最高层的kobject,而每个子节点都是其父节点的一个kobject。 -  
sysfs 映射:在 sysfs 文件系统中,这种层次结构会映射为目录和文件结构。例如,如果一个设备对象
kobject的parent字段指向一个总线对象kobject,那么在 sysfs 中该设备将会显示在相应总线目录的子目录中。 
2. parent 字段的使用示例
假设内核中有如下的 kobject 层次关系:
kobject_root(根对象)kobject_bus(总线对象)kobject_device(设备对象)
在这种情况下:
kobject_device的parent字段指向kobject_bus,表示它隶属于kobject_bus。kobject_bus的parent字段指向kobject_root,表示它隶属于根对象。
struct kobject kobject_root;
struct kobject kobject_bus;
struct kobject kobject_device;
kobject_device.parent = &kobject_bus;
kobject_bus.parent = &kobject_root;
 
3. sysfs 中的反映
假设内核对象按照上述关系组织,sysfs 文件系统中会出现以下目录结构:
/sys/kobject_root/kobject_bus/kobject_device/
 
kobject_root目录表示根kobject。kobject_bus目录是kobject_root下的子目录,表示总线对象。kobject_device目录是kobject_bus下的子目录,表示设备对象。
4. 实际场景中的应用
在实际的 Linux 内核开发中,parent 字段的应用场景包括但不限于:
- 设备树:设备和子设备的层次结构可以通过 
parent字段来表示。例如,某个总线上挂载的设备可以通过parent字段将它们组织在一起。 - 驱动模型:驱动程序中的设备对象通常会通过 
parent字段链接到其父对象,比如总线对象或设备类对象。 - 模块组织:内核模块的内部对象可以通过 
parent字段建立相互之间的层次关系。 
创建kobject 实验
示例代码
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
struct kobject *mykobj_root;
struct kobject *mykobj_child;
struct kobject *mykobj_other_root;
struct kobj_type	*myktype;
static int __init mykobj_init(void)
{
    int ret = 0;
    // 1.创建kobject第一种方法
    mykobj_root = kobject_create_and_add("mykobj_root",NULL);
    mykobj_child = kobject_create_and_add("mykobj_child",mykobj_root);
    //第二种方法
    mykobj_other_root = kzalloc(sizeof(struct kobject),GFP_KERNEL);
    ret = kobject_init_and_add(mykobj_other_root,myktype,NULL,"%s","mykobj_other_root");    
    return ret ;
}
static void __exit pmykobj_exit(void)
{
    kobject_put(mykobj_child);
    kobject_put(mykobj_root);
    kobject_put(mykobj_other_root);
}
module_init(mykobj_init); // 注意这里的分号
module_exit(pmykobj_exit); // 注意这里的分号
MODULE_AUTHOR("Marxist");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("a simple of make kobject");
 
kobject_create_and_add函数解析
 
mykobj_root = kobject_create_and_add("mykobj_root", NULL);
mykobj_child = kobject_create_and_add("mykobj_child", mykobj_root);
 
-  
kobject_create_and_add函数:这是一个便利函数,用于创建并初始化一个kobject,然后将其添加到内核对象的层次结构中。 -  
参数解释:
- 第一个参数 
"mykobj_root"和"mykobj_child"是kobject的名字。这些名字将在kobject被创建时赋值,并用于在 sysfs 文件系统中表示相应的对象。 - 第二个参数用于指定新创建的 
kobject的父对象:- 对于 
mykobj_root,父对象是NULL,表示这个kobject是根对象,不从属于任何其他kobject。 - 对于 
mykobj_child,父对象是mykobj_root,表示mykobj_child是mykobj_root的子对象。 
 - 对于 
 
 - 第一个参数 
 -  
执行结果:
mykobj_root是根kobject,将在 sysfs 中创建根目录mykobj_root。mykobj_child是mykobj_root的子对象,将在 sysfs 中作为子目录出现,即路径为/sys/mykobj_root/mykobj_child。
 
kobject_init_and_add解析
 
mykobj_other_root = kzalloc(sizeof(struct kobject), GFP_KERNEL);
ret = kobject_init_and_add(mykobj_other_root, myktype, NULL, "%s", "mykobj_other_root");
 
-  
手动分配内存:首先,通过
kzalloc函数分配了kobject所需的内存空间,并将内存初始化为零。mykobj_other_root = kzalloc(sizeof(struct kobject), GFP_KERNEL);kzalloc:分配内存并将其清零。sizeof(struct kobject)确保分配的内存大小足够存放一个kobject结构体。GFP_KERNEL:表示这是在内核空间中分配内存,并且允许在分配过程中进行阻塞(通常在内核模块中使用)。
 -  
kobject_init_and_add函数:这个函数手动初始化一个kobject,并将其添加到内核对象的层次结构中。ret = kobject_init_and_add(mykobj_other_root, myktype, NULL, "%s", "mykobj_other_root");- 第一个参数是已经分配好的 
kobject内存 (mykobj_other_root)。 - 第二个参数 
myktype是一个指向kobj_type结构体的指针,定义了kobject的行为,包括它在 sysfs 中的属性和操作。这里假设myktype已经在其他地方定义。 - 第三个参数是父 
kobject。NULL表示它是一个根对象。 - 第四个参数是格式化字符串,用于指定 
kobject的名字。在这里,"%s"会被替换为"mykobj_other_root",从而为kobject指定名称。 
 - 第一个参数是已经分配好的 
 -  
返回值:
kobject_init_and_add返回一个整数ret。如果返回值为 0,表示操作成功;如果返回负数,则表示出错,通常是由于内存分配失败或无效参数等原因。
 -  
执行结果:
mykobj_other_root是另一个根kobject,将出现在 sysfs 文件系统中,路径为/sys/mykobj_other_root。
 -  
第一种方法:使用
kobject_create_and_add简化了kobject的创建、初始化和添加过程,非常适合快速创建kobject并自动将其挂载到内核层次结构中。 -  
第二种方法:使用
kobject_init_and_add提供了更细粒度的控制,适合在需要手动管理内存或自定义kobject类型 (kobj_type) 的场景下使用。 
两种方法都能创建 kobject 并将其添加到内核对象层次中,不过第一种方法更简便,而第二种方法则更灵活。
示例效果

在sys目录下创建了mkobj_root与 mkobj_other_root
创建kset 实验
示例代码
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
struct kobject *mykobj_root;
struct kobject *mykobj_child;
struct kobject *mykobj_other_root;
struct kset *mykset;
struct kobj_type *mytype;
static int __init mykobj_init(void)
{
    int ret = 0;
    mykset = kset_create_and_add("myset",NULL,NULL);
    //申请内存
    mykobj_other_root = kzalloc(sizeof(struct kobject),GFP_KERNEL);
    mykobj_other_root->kset =  mykset;
    ret = kobject_init_and_add(mykobj_other_root,mytype,NULL,"%s","mykobj_other_root");   
    mykobj_root = kzalloc(sizeof(struct kobject),GFP_KERNEL);
    mykobj_root->kset =  mykset;
    ret = kobject_init_and_add(mykobj_root,mytype,NULL,"%s","mykobj_root");   
    return 0;
}
static void __exit pmykobj_exit(void)
{
    printk("bye bye ref -1\n");
    kobject_put(mykobj_root);
    kobject_put(mykobj_other_root);
    kset_unregister(mykset);
}
module_init(mykobj_init);
module_exit(pmykobj_exit);
MODULE_AUTHOR("Marxist");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("a simple example of making kset");
 
kset_create_and_add 是 Linux 内核中用于创建并添加一个 kset 的函数。它的原型如下:
c复制代码struct kset *kset_create_and_add(const char *name, 
                                 const struct kset_uevent_ops *uevent_ops,
                                 struct kobject *parent);
 
参数解析
const char \*name:kset的名称,用于标识该kset。这个名称将用于 sysfs 中对应的目录名。
const struct kset_uevent_ops \*uevent_ops:- 这是一个指向 
kset_uevent_ops结构体的指针,该结构体包含了kset处理用户空间事件(如 uevent)的回调函数指针。如果不需要处理 uevent,这里可以传NULL。 
- 这是一个指向 
 struct kobject \*parent:- 该 
kset的父kobject,用于指定该kset在 sysfs 中的层次结构。如果这个kset是一个顶层对象,则可以传NULL。 
- 该 
 
示例效果

因为创建kobject的时候,传入的参数为NULL,因此交给了kest管理,所以在myset这个目录下能找到这两个obj







![学习方法[2]:如何有效地检索及选择学习资料?(致在自学之路仍在坚持的人)](https://i-blog.csdnimg.cn/direct/efd0fea2b1784a63be196854c7fe5c41.png)











