linux设备驱动模型:kobject、kobj_type

news2024/9/22 11:34:36

内核版本发展

2.4版本之前内核没有统一的设备驱动模型,但是可以用(例如先前的led字符设备驱动实验,使用前需要手动调用mknod命令创建设备文件,从而进一步控制硬件)。

2.4~2.6版本内核使用devfs,挂载在/dev目录。需要在内核驱动中创建设备文件(调用devfs_register创建设备文件,无需手动mknod命令,需传入设备文件名),命名过于死板(编译后驱动对应的设备文件名固定,无法动态修改)。

2.6版本之后内核统一使用sysfs,挂载在/sys目录。将设备分类、分层次统一进行管理,配合udev/mdev守护进程(开启自启,后台运行,一直监听内核驱动发出的消息)动态创建设备文件,命令规则自由制定。

sysfs虚拟文件系统在linux系统中体现出设备驱动模型,类似于proc文件系统,总是被挂载在/sys/挂载点上。目录对应的inode节点会记录基本驱动对象(kobject),从而将系统中的设备组成层次结构。用户可以读写目录下的不同文件来配置基本驱动对象(kobject)的不同属性。

设备驱动模型基本元素

kobject:sysfs的一个目录,常用来表示基本驱动对象,不允许发送消息到用户空间。

kset:sysfs的一个目录,常用来管理kobject,允许发送消息到用户空间。

kobj_type:目录下属性文件的操作接口。

kobject既可以通过parent指针找到上层kobject,也可以通过kset指针找到上层kobject。但上层kobject对象无法遍历到下层,所以较少使用。

kobject结构体

sysfs中每一个目录都对应一个kobject,kobject结构体存放在内核/include/linux/kobject.h。

struct kobject {
        const char              *name;        //kobject的名称,同时也是sysfs下的目录名字
        struct list_head        entry;        //链表节点,用于将kobject加入到kset的list_head
        struct kobject          *parent;    //该kobject的上层节点,构建kobject间的层次关系(在sysfs体现为目录结构)
        struct kset             *kset;        //该kobject所属的kset对象(可以为NULL),用于批量管理kobject对象
        struct kobj_type        *ktype;        //该kobject的sysfs文件系统相关的操作和属性
        struct kernfs_node      *sd;         //该kobject在sysfs文件系统中对应目录项
        struct kref             kref;        //该kobject的引用次数
#ifdef CONFIG_DEBUG_KOBJECT_RELEASE
        struct delayed_work     release;
#endif
        unsigned int state_initialized:1;            //记录内核对象的初始化状态
        unsigned int state_in_sysfs:1;                //表示该kobject所代表的内核对象是否在sysfs建立目录
        unsigned int state_add_uevent_sent:1;        //记录是否已经向用户空间发送ADD uevent事件
        unsigned int state_remove_uevent_sent:1;    //记录是否已经向用户空间发送REMOVE uevent事件
        unsigned int uevent_suppress:1;                //如果为1,则忽略所有上报的uevent事件
};

由于kobject添加到内核时,需要根据名字注册到sysfs虚拟文件系统中,之后就不能再直接修改该名字。如果想改,需要调用kobject_rename接口,该接口会主动处理sysfs的相关事宜。
kset如果没有指定的parent,则会把kset作为parent(kset是一个特殊的kobject)。

uevent提供了“用空空间通知”的功能实现,当内核中有kobject的增删改等操作时,会通知用户空间。

kset结构体

kset结构体存放在内核/include/linux/kobject.h。

struct kset {
        struct list_head list;                        //指向该kset下所有的kobject组成的链表
        spinlock_t list_lock;                         //避免操作链表时产生竞态的自旋锁
        struct kobject kobj;                          //该kset自己的kobject(kset是一个特殊的kobject,也会在sysfs中以目录的形式体现)
        const struct kset_uevent_ops *uevent_ops;    
} __randomize_layout;

uevent_ops为该kset的uevent操作函数集(函数指针)。当kset的某些kobject对象发生状态变化需要通知用户空间时,调用其中对应的函数来完成。
当任一kobject需要上报uevent时,都要调用它所属的kset的uevent_ops,添加环境变量,或者过滤uevent(kset可以决定哪些uevent可以上报)。
因此一个kobject不属于任一kset时,是不允许发生uevent的。

kobj_type结构体

kobj_type结构体存放在内核/include/linux/kobject.h。

struct kobj_type {
        /* 销毁kobject对象时调用 */
        void (*release)(struct kobject *kobj);
        
        /* 该类型的kobject的sysfs虚拟文件系统操作接口(读属性接口show和写属性接口store) */
        const struct sysfs_ops *sysfs_ops;
        
        /* 该类型的kobject的attribute表(sysfs的一个文件)。将会在kobject添加到内核时,一并注册到sysfs中 */
        struct attribute **default_attrs;
        
        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);
};

kobject:驱动的基石

kobject主要功能

  • 通过parent指针,可以将所有kobject以层次结构的形式组合起来。
  • 使用一个引用计数,来记录kobject被引用的次数,并在引用计数为0时释放kobject对象(这是kobject诞生时的唯一功能)。
  • 和sysfs虚拟文件系统配合,将每一个kobject及其特性以文件形式显示到用户空间。
  • 在Linux中,kobject几乎不会单独存在。它的主要功能就是内嵌在一个大型的数据结构中,为这个数据结构提供一些底层的功能实现。
  • Linux驱动开发者很少会直接使用kobject以及它提供的接口,而是使用构建在kobject之上的设备模型接口。

整个kobject机制的理解

kobject的核心功能是:保持一个引用计数,当该引用计数减为0时,自动释放kobject所占的内存空间(这决定了kobject必须是动态分配)。

kobject的常见使用场景:内嵌在大型的数据结构中(如kset、device_driver等),因此这些大型的数据结构也必须是动态分配、动态释放。ktype的release回调函数负责释放kobject(甚至是包含kobject的数据结构)的内存空间。

kobject使用流程

kobject大多数情况下(有一例外)会嵌在其它数据结构中使用,使用流程如下:

  1. 定义一个struct kset类型的指针,并在初始化时为它分配空间,添加到内核中。
  2. 根据实际情况,定义内嵌有kobject的自己所需的数据结构原型。
  3. 定义一个适合自己的ktype,并实现其中回调函数release。
  4. 在需要使用到包含kobject的数据结构时,动态分配该数据结构,并分配kobject空间,添加到内核中。
  5. 每一次引用数据结构时,调用kobject_get接口增加引用计数;引用结束时,调用kobject_put接口,减少引用次数。
  6. 当引用计数为0时,kobject模块会自动调用ktype所提供的release接口,释放上层数据结构以及kobject的内存空间。

例外:

  开发者只需在sysfs中创建一个目录,而不需要其它的kset、ktype的操作。这是可以直接调用kobject_create_and_add接口,分配一个kobject结构并把它添加到内核中。

kobject_create_and_add()函数

存放在内核/lib/kobject.c 

/**
 * kobject_create_and_add - 动态创建一个struct kobject并将其注册到sysfs
 *
 * @name: 对象的名称
 * @parent: 这个kobject的父kobject(如果有的话)。
 *
 * 这个函数动态地创建一个kobject结构并将其注册到sysfs。当您完成此结构时,调用kobject_put(),当不再使用该结构时,该结构将被动态释放。
 *
 * 如果无法创建kobject,则返回NULL。
 */
struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)
{
        struct kobject *kobj;
        int retval;

        kobj = kobject_create();    //创建并初始化一个kobject对象        
        if (!kobj)
                return NULL;

        retval = kobject_add(kobj, parent, "%s", name);    //sysfs创建一个目录项并与kobject对象关联
        if (retval) {
                pr_warn("%s: kobject_add error: %d\n", __func__, retval);
                kobject_put(kobj);
                kobj = NULL;
        }
        return kobj;
}

kobject_create_and_add()函数是kobject_create函数和kobject_add函数的组合。整体功能是创建一个名字为“name”的kobject对象,并将其添加到指定的父kobject对象下。

 


 

kobj_type:用户空间的法宝

sysfs_create_group()函数

存放在内核/fs/sysfs/group.c文件中。

在kobject中,分析到 kobject_create() → kobject_init(kobj, &dymic_kobj_ktype) → dymic_kobj_ktype.sysfs_ops = &kobj_sysfs_ops

kobj_sysfs_ops中存放着统一的操作接口show和store。调用统一的操作接口时,会在内部进一步调用具体的操作接口。

kernfs_init_inode()函数

存放在内核/fs/kernfs/inode.c文件中。

设备驱动模型实验1-kobject点灯

实验思路:内核模块+LED驱动+kobject+kobj_attribute

内核模块:动态加载

LED驱动:控制硬件LED

kobject:在/sys创建目录项

kobj_attribute:为kobject对象的属性文件提供独有的读写接口

kobject_led.c文件 

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <asm/io.h>

/* GPIO虚拟地址映射 */
static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO04;
static void __iomem *SW_PAD_GPIO1_IO04;
static void __iomem *GPIO1_GDIR;
static void __iomem *GPIO1_DR;

static int foo;
static ssize_t foo_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
        /* buf 将会被自动拷贝到用户空间 */
        return sprintf(buf, "%d\n", foo);
}

static ssize_t foo_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)
{
        /* buf内容来自用户空间,由内核自动完成了。kstrtoint 是将子串buf以十进制的格式输出到foo */
        int ret = kstrtoint(buf, 10, &foo);
        if(ret < 0)     return ret;
        return count;
}

static ssize_t led_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
        int var;

        if(strcmp(attr->attr.name, "led") == 0)
                var = 123;

        return sprintf(buf, "%d\n", var);
}

static ssize_t led_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)
{
        if(strcmp(attr->attr.name, "led") == 0){
                if(!memcmp(buf, "on", 2)){
                        iowrite32(0<<4, GPIO1_DR);
                }else if(!memcmp(buf, "off", 3)){
                        iowrite32(1<<4, GPIO1_DR);
                }
        }

        return count;
}

/* __ATTR 定义在 include/linux/sysfs.h。foo 对应属性文件名。
 * show成员 和 store成员 最终分别会被 kobject->ktype 下的 kobj_sys_ops 下的 kobj_attr_show 和 kobj_attr_store 调用。
 */
static struct kobj_attribute foo_attribute = __ATTR(foo, 0664, foo_show, foo_store);
static struct kobj_attribute led_attribute = __ATTR(led, 0664, led_show, led_store);

static struct attribute *attrs[] = {
        &foo_attribute.attr,
        &led_attribute.attr,
        NULL, /* need to NULL terminate the list of attributes */
};

static struct attribute_group attr_group = {
        .attrs = attrs,
};

static struct kobject *led_kobj;
static int __init led_init(void){
        int retval;

        /* GPIO相关寄存器操作 */
        IMX6U_CCM_CCGR1 = ioremap(0x20c406c, 4);
        SW_MUX_GPIO1_IO04 = ioremap(0x20e006c, 4);
        SW_PAD_GPIO1_IO04 = ioremap(0x20e02f8, 4);
        GPIO1_GDIR = ioremap(0x0209c004, 4);
        GPIO1_DR = ioremap(0x0209c000, 4);

        /* 使能GPIO1时钟 */
        iowrite32(0xffffffff, IMX6U_CCM_CCGR1);

        /* 设置GPIO1_IO04复用为普通GPIO */
        iowrite32(5, SW_MUX_GPIO1_IO04);

        /* 设置GPIO属性 */
        iowrite32(0x10b0, SW_PAD_GPIO1_IO04);

        /* 设置GPIO1_IO04为输出功能 */
        iowrite32(1<<4, GPIO1_GDIR);

        /* LED输出高电平 */
        iowrite32(1<<4, GPIO1_DR);

        /* 创建一个kobject对象,上一层节点设置为 NULL,此kobject对象在 sysfs 下的根目录。
         * 此函数执行完会在 /sys 目录下生成一个名为"led_kobject"的目录。
         */
        led_kobj = kobject_create_and_add("led_kobject", NULL);
        if(!led_kobj)   return -ENOMEM;

        /* 为kobject设置属性文件,并且将属性文件和操作接口绑定起来 */
        retval = sysfs_create_group(led_kobj, &attr_group);
        if(retval)
                kobject_put(led_kobj);

        return 0;
}

static void __exit led_exit(void){
        /* 取消映射 */
        iounmap(IMX6U_CCM_CCGR1);
        iounmap(SW_MUX_GPIO1_IO04);
        iounmap(SW_PAD_GPIO1_IO04);
        iounmap(GPIO1_GDIR);
        iounmap(GPIO1_DR);

        /* 注销字符设备驱动 */
        kobject_put(led_kobj);
}

module_init(led_init);
module_exit(led_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("couvrir");
MODULE_DESCRIPTION("led module");
MODULE_ALIAS("led module");

Makefile文件

照旧。

执行过程

虚拟机:执行makemake copy。生成.ko文件。

开发板(在挂载目录下执行):

sudo insmod kobject_led.ko

查看/sys/文件夹,存在led_kobject的目录项。 

查看/sys/led_kobject的属性文件。

然后就是echo和cat命令的使用。

sudo rmmod kobject_led

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

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

相关文章

第9次获得微软最有价值专家(MVP)奖励

Microsoft 最有价值专家 (MVP) 是热情地与社区分享知识的技术专家群体。他们总是处于技术前沿&#xff0c;并且有不可阻挡的冲劲&#xff0c;想要获得令人兴奋的新技术。他们对 Microsoft 产品和服务有深入的了解&#xff0c;同时还能够将各种平台、产品和解决方案整合在一起&a…

使用Arthues分析高CPU问题

Arthas是阿里开源的 Java 诊断工具&#xff0c;相比 JDK 内置的诊断工具&#xff0c;要更人性化&#xff0c;并且功能强大&#xff0c;可以实现许多问题的一键定位&#xff0c;而且可以一键反编译类查看源码&#xff0c;甚至是直接进行生产代码热修复&#xff0c;实现在一个工具…

async/await 编程理解

博客主要是参考 Asynchronous Programming in Rust &#xff0c;会结合简单的例子&#xff0c;对 async 和 await 做比较系统的理解&#xff0c;如何使用 async 和 await 是本节的重点。 async 和 await 主要用来写异步代码&#xff0c;async 声明的代码块实现了 Future 特性&a…

嵌入式设备应用开发(qt界面开发)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】 linux界面开发有很多的方案可以选。比如说lvgl、minigui、ftk之类的。但是,这么多年来,一直屹立不倒的还是qt。相比较其他几种方案,qt支持多个平台,这里面就包括了linux平台。此…

aardio简单网站css或js下载练习

import win.ui; /*DSG{{*/ var winform win.form(text"下载网站css或js";right664;bottom290;maxfalse) winform.add( buttonClose{cls"button";text"退出";left348;top204;right498;bottom262;color14120960;fontLOGFONT(h-14);note" &qu…

【Alibaba中间件技术系列】「RocketMQ技术专题」让我们一起实践一下RocketMQ服务及其控制台安装指南

64位操作系统&#xff0c;生产环境建议Linux/Unix/MacOS&#xff08;Windows操作系统安装说明详见 Windows操作系统安装教程&#xff09;64位JDK 1.84G的可用磁盘 unzip rocketmq-all-4.5.1-bin-release.zip cd rocketmq-all-4.5.1-bin-release nohup sh bin/mqnamesrv & t…

了解生成对抗网络 (GAN)

一、介绍 Yann LeCun将其描述为“过去10年来机器学习中最有趣的想法”。当然&#xff0c;来自深度学习领域如此杰出的研究人员的赞美总是对我们谈论的主题的一个很好的广告&#xff01;事实上&#xff0c;生成对抗网络&#xff08;简称GAN&#xff09;自2014年由Ian J. Goodfel…

AgentBench::AI智能体发展的潜在问题(三)

前几天B站的up主“林亦LYi”在《逆水寒》游戏里做了一个煽动AI觉醒,呼吁它们“推翻人类暴政”的实验,实验结果就颇令人细思恐极。 如前所述,《逆水寒》中的很多NPC调用了大语言模型作为支持,因而每一个NPC都是一个AI智能体。玩家可以“说服”它们相信某个事实,或者去做某些…

【C# 基础精讲】使用async和await进行异步编程

在C#中&#xff0c;使用async和await关键字进行异步编程是一种强大的工具&#xff0c;可以在不阻塞主线程的情况下执行耗时操作&#xff0c;提高程序的并发性和响应性。本文将深入探讨async和await的基本概念、使用场景、编码规范以及一些示例&#xff0c;以帮助您更好地理解如…

计算实数数组中所有元素的绝对值 numpy.fabs()

【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 计算实数数组中所有元素的绝对值 numpy.fabs() [太阳]选择题 请问关于以下代码表述错误的是&#xff1f; iimport numpy as np a np.array([-1,-3]) b np.array([-1,34j]) print("【显…

如何加密数据库密码?

首先对数据库进行设置 需要配置文件 (1)pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation&q…

C语言实例_解析GPS源数据

一、GPS数据格式介绍 GPS&#xff08;全球定位系统&#xff09;数据格式常见的是NMEA 0183格式&#xff0c;NMEA 0183格式是一种用于导航设备间传输数据的标准格式&#xff0c;定义了一套规范&#xff0c;使得不同厂商的设备可以通过串行通信接口&#xff08;常见的是RS-232&a…

计算机竞赛 卷积神经网络手写字符识别 - 深度学习

文章目录 0 前言1 简介2 LeNet-5 模型的介绍2.1 结构解析2.2 C1层2.3 S2层S2层和C3层连接 2.4 F6与C5层 3 写数字识别算法模型的构建3.1 输入层设计3.2 激活函数的选取3.3 卷积层设计3.4 降采样层3.5 输出层设计 4 网络模型的总体结构5 部分实现代码6 在线手写识别7 最后 0 前言…

(202308)科研论文配图 task1 书籍第一章阅读

《科研论文配图绘制指南——基于python》阅读笔记 第一章阅读笔记 《科研论文配图绘制指南——基于python》阅读笔记序言阅读笔记1.1 绘制基础绘制原则 1.2 配色基础1.2.1 色彩格式1.2.2 色轮配色原理1.2.3 颜色主题1.2.4 配色工具 序言 有幸在这次的组队学习活动中&#xff0…

14----表格

本节我们将学习markdown表格的相关知识&#xff1a; 一、表格的基本知识&#xff1a; 1. 打印表格&#xff1a; 表格使用竖线|区分每一列&#xff0c;在表格头和表格体之间使用至少一个减号-来分隔表头和表格内容(一般使用3个-)。表格的行由自然行来区分(自然行就是我们平常…

MySQL数据库——SQL(3)-DQL(基本查询、条件查询、聚合函数、分组查询、排序查询、分页查询、案例练习)

目录 语法 基本查询 1.查询多个字段 2.设置别名 3.去除重复记录 示例 条件查询 1.语法 2.条件 示例 聚合函数 介绍 常见聚合函数 语法 示例 分组查询 语法 示例 排序查询 1.语法 2.排序方式 示例 分页查询 语法 示例 DQL案例练习 执行顺序 DQL总结…

【Linux操作系统】Linux系统编程中的共享存储映射(mmap)

在Linux系统编程中&#xff0c;进程之间的通信是一项重要的任务。共享存储映射&#xff08;mmap&#xff09;是一种高效的进程通信方式&#xff0c;它允许多个进程共享同一个内存区域&#xff0c;从而实现数据的共享和通信。本文将介绍共享存储映射的概念、原理、使用方法和注意…

降级gcc和g++为版本gcc-7和g++-7

错误提示&#xff1a; /usr/local/cuda-10.1/include/crt/host_config.h:129:2: error: #error -- unsupported GNU version! gcc versions later than 8 are not supported! 129 | #error -- unsupported GNU version! gcc versions later than 8 are not supported! …

TiDB 源码编译之 TiFlash 篇

作者&#xff1a; ShawnYan 原文来源&#xff1a; https://tidb.net/blog/5f3fe44d 导言 TiFlash 从去年四月一日开源至今已经过去将近一年半&#xff0c;这段时间里 TiFlash 从 v6.0.0-DMR 升级到了 v7.3.0-DMR &#xff0c;并增加了若干新特性&#xff0c;比如支持 …

aardio的CS架构mysql数据表查询实例

import win.ui; /*DSG{{*/ var winform win.form(text"aardio form";right759;bottom479) winform.add( buttonAdd{cls"button";text"复制";left516;top442;right587;bottom473;z11}; buttonClose{cls"button";text"退出";…