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

news2024/11/14 15:09:17

文章目录

    • 什么是设备模型?
      • 设备模型的主要组成部分
      • 设备模型的关键功能
      • 设备模型的实现结构
      • 设备模型的重要性
    • 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 内核中的一个抽象层,用于统一管理和组织系统中的各种硬件设备及其驱动程序。它为设备、驱动、总线和电源管理等提供了一个统一的接口和结构,使得内核能够更加高效和一致地管理系统中的硬件资源。

设备模型的主要组成部分

  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 允许在其被释放时执行特定的回调函数。
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
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,表示它隶属于根对象。
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_childmykobj_root 的子对象。
  • 执行结果

    • mykobj_root 是根 kobject,将在 sysfs 中创建根目录 mykobj_root
    • mykobj_childmykobj_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 已经在其他地方定义。
    • 第三个参数是父 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2054693.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

算法的学习笔记—对称的二叉树(牛客JZ28)

&#x1f600;前言 在算法的世界中&#xff0c;二叉树是一个极其重要的数据结构。它不仅广泛应用于各种算法的设计中&#xff0c;也是面试中常见的考察点之一。今天&#xff0c;我们将深入探讨一个经典的二叉树问题——对称的二叉树&#xff0c;并且会展示如何通过Java代码来解…

stripe Element 如何使用

这里要准备好几个东西&#xff1a; 一个支付成功过后的回调 还有一个下单的接口 一旦进入这个下单界面&#xff0c;就要去调下单的接口的&#xff0c;用 post, 这个 接口你自己写&#xff0c;可以写在后端中&#xff0c;也可以放到 nextjs 的 api 中。 首先说的是这个下单…

去中心化的新时代:Web3技术的全球影响

随着技术的不断演进&#xff0c;Web3正引领互联网的去中心化新时代。相较于传统的Web1和Web2&#xff0c;Web3通过去中心化、区块链和智能合约等技术&#xff0c;正在重塑网络的运作方式。这一变革不仅提升了网络的安全性和透明度&#xff0c;也对全球经济、社会和文化产生了深…

品牌出海新策略:携手TikTok达人,合作孵化IP实现双赢

在当今数字化时代&#xff0c;TikTok达人的IP孵化作为一种创新的合作模式&#xff0c;正逐渐成为品牌出海的新兴策略。通过与有潜力的TikTok达人合作&#xff0c;共同孵化新的IP&#xff0c;品牌不仅能够突破传统营销的局限&#xff0c;还能实现与达人共同成长的双赢局面。本文…

物流抓取机器人整体设计方案

一、功能简介 1、运行环境&#xff1a;巡线行驶&#xff08;7路数字循迹&#xff0c;麦克纳姆轮车底盘&#xff09; 2、目标识别&#xff1a;颜色识别&#xff08;Maix-II Dock 视觉模块&#xff09; 3、目标定位&#xff1a;视觉测距&#xff08;Maix-II Dock 视觉模块&#x…

海外直播对网络的要求有哪些?

在全球化的大潮中&#xff0c;海外直播已成为越来越多企业、个人和机构展示自身、拓展市场、与全球用户互动的重要渠道。然而&#xff0c;在进行海外直播时&#xff0c;网络环境的搭建往往成为制约其成功与否的关键因素。那么&#xff0c;究竟什么样的网络环境才能满足海外直播…

Python使用Selenium进行Web自动化测试详解

目录 引言 一、Selenium简介 Selenium的核心组件 二、环境搭建 1. 安装Python 2. 安装Selenium库 3. 下载并配置浏览器驱动 三、基础用法 1. 启动浏览器 2. 定位页面元素 3. 元素操作 4. 等待元素加载 1. 测试目的 2. 测试步骤与代码实现 3. 注意事项 结论 引言…

学习方法[2]:如何有效地检索及选择学习资料?(致在自学之路仍在坚持的人)

有效地检索及选择学习资料 前言一、如何进行有效检索资料&#xff1f;&#xff08;以bing为例&#xff09;1.1 基础搜索1.2 高级搜索1.2.1 高级搜索关键字1.2.2 高级搜索选项 二、如何选择学习资料&#xff1f;&#xff08;以编程为例&#xff09;2.1 源代码2.2 官方文档2.3 英…

Quartz - 定时任务框架集成

参考了若依框架&#xff0c;将quartz定时任务框架集成到自己的项目当中。 目录 一、Quartz概述二、库表创建1.Quartz关键表&#xff08;11张&#xff09;表SQL 2.自定义业务表&#xff08;2张&#xff09;表SQL 三、代码示例1.依赖引入2.类文件1&#xff09;定时任务配置类2&am…

优优嗨聚集团:餐饮合作新未来引领美食产业新风尚

在快速变化的21世纪&#xff0c;餐饮行业作为民生消费的重要组成部分&#xff0c;正经历着前所未有的变革与挑战。随着消费者需求的多元化、个性化以及科技的不断进步&#xff0c;餐饮合作的新模式正悄然兴起&#xff0c;为行业带来了前所未有的发展机遇与活力。本文将探讨餐饮…

如何复现Github上的项目以及conda的常用操作指令

在GitHub上关于深度学习的项目代码通常包含多种类型的文件&#xff0c;每种文件都有其特定的作用。以下是一些常见的文件及其作用的概述&#xff1a; 一个常用的项目结构如下&#xff1a; --project_name/ &#xff1a;项目名----data/&#xff1a;数据集--------__init__.py…

vue+elementui 主题配色修改-打造个性化配色系统

上一期中利用global.css来覆盖elementui原有的配色&#xff0c;修改了按钮和消息框。这一期继续尝试修改其他的控件。 1 修改info 类型按钮 上次修改了primary按钮&#xff0c;这次修改一下info按钮&#xff0c;在global.css中添加 .el-button--info {background-color: #d9d…

deepspeed的并行模式介绍笔记

1.整体框架 2.并行模式 1.数据并行DDP 数据切分以后&#xff0c;分开单张卡训练得到参数&#xff0c;然后综合在单卡计算。 要点&#xff1a;前向计算和反向计算两步骤走并汇总。 1.前向计算 需要留一块主卡一定空间用于综合。 2.反向传播 利用前向传播的汇总参数得到各个…

深度学习基础—超参数调试

1.超参数调试顺序 在训练深度网络最难的事情之一是超参数的选择&#xff0c;如何选择合适的超参数取值&#xff1f;下面我将谈谈&#xff0c;如下是我所理解的超参数调试顺序&#xff1a; 重要性排序 超参数 Top1梯队 学习率a Top2梯队 min-batch大小&#xff0c;隐层神经…

10 VS Code 调试技巧之逐断点、逐过程、单步调试与单步跳出

目录 1 断点调试 1.1 断点调试介绍 1.2 如何设置断点 1.3 如何开启调试 2 调试类型 2.1 逐断点调试 2.2 逐过程调试 2.3 单步调试 2.4 单步跳出 1 断点调试 遇到难以捉摸的软件错误时&#xff0c;老练的程序员会推荐断点调试。通过设置断点&#xff0c;逐步跟踪…

nvidia jetson 系列开发板交叉编译方法,CUDA依赖程序

资源 Toolchain Information jetson-linux jetpack 文章目录 资源1 方案1 qemu-aarch64-static和docker 容器编译jetson2 方案2 模拟器交叉编译器2.1 应对库缺失的情况&#xff0c;进行环境准备2.1.1 模拟器(方案1)2.1.2 在jetson上面进行安装&#xff08;方案2&#xff09;2.…

如何有效清理宝塔控制面板中的垃圾文件与优化系统性能

宝塔控制面板&#xff08;BT-Panel&#xff09;作为一款流行的服务器管理软件&#xff0c;极大地简化了Linux服务器的管理任务&#xff0c;包括网站部署、数据库管理、文件操作等。然而&#xff0c;随着服务器运行时间的增长&#xff0c;系统中会积累各种临时文件、日志文件、缓…

STM32G474按钮输入和点灯

在获取到工程模板后&#xff0c;学习某个CPU的第一步通常都是IO口操作。因此按钮输入和点灯&#xff0c;就是本次学习的第一个程序。先从简单入手。 和GPIO操作有关的函数如下: __HAL_RCC_GPIOA_CLK_ENABLE();//使能GPIOA时钟 __HAL_RCC_GPIOB_CLK_ENABLE();//使能GPIOB时钟 _…

Redis7基础篇(四)

Redis管道 引入 set k1 v1.......需要往返三次 这是一个比较消耗性能的一件事情 怎么就可以一次性的将这些命令执行 就是使用mset这个方法 这个mset就相当于一个管道 把这些命令做成一个流水线的形式进行处理 解决思路 案例 两个set类型的三个哈希类型的一个list类型的 我们要…

知乎信息流广告效果如何?与其他信息流广告平台有何区别?

广告的有效触达与高效转化成为了品牌营销的核心挑战&#xff0c;知乎作为国内最大的知识分享平台&#xff0c;其信息流广告以其独特的优势脱颖而出&#xff0c;成为众多企业首选的营销工具&#xff0c;云衔科技助力企业实现高效知乎广告投放与代运营服务。 一、知乎信息流广告…