Linux驱动开发—设备模型框架 kset和 kobject 详解

文章目录

什么是设备模型?

设备模型(Device Model)是 Linux 内核中的一个抽象层,用于统一管理和组织系统中的各种硬件设备及其驱动程序。它为设备、驱动、总线和电源管理等提供了一个统一的接口和结构,使得内核能够更加高效和一致地管理系统中的硬件资源。

设备模型的主要组成部分

  1. 设备(Device)

    • 表示系统中的一个硬件设备,比如一个硬盘驱动器、网络适配器、USB 设备等。每个设备在内核中通常用一个 struct device 结构体来表示。设备与驱动程序、总线都有关系,通常设备会挂载在某个总线上。
  2. 驱动(Driver)

    • 表示控制设备的程序代码。每个驱动程序都与特定类型的设备关联,驱动程序通过特定的总线接口与设备进行交互。驱动程序在内核中通常用 struct device_driver 结构体来表示。
  3. 总线(Bus)

    • 设备和驱动程序之间的通信通道。总线将设备与相应的驱动程序连接起来,内核通过总线来匹配设备和驱动程序。总线在内核中用 struct bus_type 结构体来表示。
  4. 类(Class)

    • 表示一组具有相似特性的设备,这些设备可能分布在不同的总线上。类为用户空间提供了一种查看和管理设备的方式。每个类在内核中用 struct class 结构体来表示。
  5. 电源管理(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内核中,设备模型框架主要通过kobjectkset来组织和管理系统中的各种设备和子系统。这些抽象提供了一种统一的方式来表示内核对象,支持系统中设备和内核组件的层次化组织。以下是kobjectkset的详细介绍:

1. kobject

kobject 是 Linux 内核中表示一个对象的基础结构体,几乎所有的内核对象都可以用 kobject 来表示。kobject 提供了内核对象的基本属性和功能,包括:

  • 引用计数kobject 通过引用计数来管理对象的生命周期,防止对象在未释放前被删除。
  • 名字和路径 :每个 kobject 都有一个唯一的名字,并可以在内核的 sysfs 文件系统中显示。
  • 关联的 ksetkobject 可以被添加到一个 kset 中,这样就能将 kobject 组织成一个集合。
  • 回调函数kobject 允许在其被释放时执行特定的回调函数。
c 复制代码
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

ksetkobject 的集合,表示一组相关联的 kobjectkset 提供了一种将相关对象组织在一起的机制,这些对象通常共享相同的父对象,并具有相似的操作。

  • kset 的结构kset 本质上是一个包含多个 kobject 的容器,并且还可以定义一些与这些对象相关的操作。
  • 管理机制kset 管理 kobject 的创建和销毁,还能够在集合中添加或移除 kobject
c 复制代码
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. 应用场景

  • 设备和驱动模型kobjectkset 在 Linux 设备模型中被广泛使用,用于组织设备(例如 PCI 设备、USB 设备)和驱动程序,并在 sysfs 文件系统中展示它们的关系。
  • sysfs 目录结构kobjectkset 是 sysfs 文件系统的基础,内核中的许多对象(如设备、驱动程序、子系统)都会在 sysfs 中以文件或目录的形式表示,而这些文件和目录背后就是 kobjectkset 的实现。

kobject中parent概念

kobject 结构体中的 parent 字段表示该 kobject 的父对象。这一字段的作用是帮助构建和维护内核对象的层次结构,使得不同的内核对象能够以树形结构组织起来。这种层次结构有助于理清内核对象之间的关系,并在文件系统(如 sysfs)中反映这些关系。

1. parent 字段的作用

  • 层次结构parent 字段将当前 kobject 与它的父对象连接起来,形成一个层次化的结构。通过这种结构,内核对象可以形成类似树的组织形式,其中根节点是最高层的 kobject,而每个子节点都是其父节点的一个 kobject

  • sysfs 映射 :在 sysfs 文件系统中,这种层次结构会映射为目录和文件结构。例如,如果一个设备对象 kobjectparent 字段指向一个总线对象 kobject,那么在 sysfs 中该设备将会显示在相应总线目录的子目录中。

2. parent 字段的使用示例

假设内核中有如下的 kobject 层次关系:

  • kobject_root (根对象)
    • kobject_bus (总线对象)
      • kobject_device (设备对象)

在这种情况下:

  • kobject_deviceparent 字段指向 kobject_bus,表示它隶属于 kobject_bus
  • kobject_busparent 字段指向 kobject_root,表示它隶属于根对象。
c 复制代码
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函数解析

c 复制代码
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_childmykobj_root 的子对象。
  • 执行结果

    • mykobj_root 是根 kobject,将在 sysfs 中创建根目录 mykobj_root
    • mykobj_childmykobj_root 的子对象,将在 sysfs 中作为子目录出现,即路径为 /sys/mykobj_root/mykobj_child

kobject_init_and_add解析

c 复制代码
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 所需的内存空间,并将内存初始化为零。

    c 复制代码
    mykobj_other_root = kzalloc(sizeof(struct kobject), GFP_KERNEL);
    • kzalloc:分配内存并将其清零。sizeof(struct kobject) 确保分配的内存大小足够存放一个 kobject 结构体。
    • GFP_KERNEL:表示这是在内核空间中分配内存,并且允许在分配过程中进行阻塞(通常在内核模块中使用)。
  • kobject_init_and_add 函数 :这个函数手动初始化一个 kobject,并将其添加到内核对象的层次结构中。

    c 复制代码
    ret = kobject_init_and_add(mykobj_other_root, myktype, NULL, "%s", "mykobj_other_root");
    • 第一个参数是已经分配好的 kobject 内存 (mykobj_other_root)。
    • 第二个参数 myktype 是一个指向 kobj_type 结构体的指针,定义了 kobject 的行为,包括它在 sysfs 中的属性和操作。这里假设 myktype 已经在其他地方定义。
    • 第三个参数是父 kobjectNULL 表示它是一个根对象。
    • 第四个参数是格式化字符串,用于指定 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_rootmkobj_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);

参数解析

  1. const char \*name :
    • kset 的名称,用于标识该 kset。这个名称将用于 sysfs 中对应的目录名。
  2. const struct kset_uevent_ops \*uevent_ops :
    • 这是一个指向 kset_uevent_ops 结构体的指针,该结构体包含了 kset 处理用户空间事件(如 uevent)的回调函数指针。如果不需要处理 uevent,这里可以传 NULL
  3. struct kobject \*parent :
    • kset 的父 kobject,用于指定该 kset 在 sysfs 中的层次结构。如果这个 kset 是一个顶层对象,则可以传 NULL

示例效果

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

相关推荐
IC 见路不走3 分钟前
LeetCode 第91题:解码方法
linux·运维·服务器
翻滚吧键盘16 分钟前
查看linux中steam游戏的兼容性
linux·运维·游戏
小能喵20 分钟前
Kali Linux Wifi 伪造热点
linux·安全·kali·kali linux
汀沿河35 分钟前
8.1 prefix Tunning与Prompt Tunning模型微调方法
linux·运维·服务器·人工智能
zly35001 小时前
centos7 ping127.0.0.1不通
linux·运维·服务器
小哥山水之间2 小时前
基于dropbear实现嵌入式系统ssh服务端与客户端完整交互
linux
ldj20202 小时前
2025 Centos 安装PostgreSQL
linux·postgresql·centos
翻滚吧键盘2 小时前
opensuse tumbleweed上安装显卡驱动
linux
cui_win3 小时前
【内存】Linux 内核优化实战 - net.ipv4.tcp_tw_reuse
linux·网络·tcp/ip
CodeWithMe6 小时前
【Note】《深入理解Linux内核》 Chapter 15 :深入理解 Linux 页缓存
linux·spring·缓存