Linux内核编程(十一)设备模型

news2024/9/20 9:03:31

本文目录

  • 一、知识点
    • 1. 设备模型
    • 2. sysfs 文件系统
    • 3. kobject、kset设备模型框架
  • 二、kobject实验
    • 1. 创建kobject
    • 2. 释放kobject
    • ★示例
  • 三、kset实验
    • 1. 创建kset
    • 2. 注销kset
    • ★示例
  • 四、引用计数器
    • 1. 概念
    • 2. 为什么要引入引用计数器?
    • 3. 常用函数
      • (1)初始化计数器值为1
      • (2)计数器值+1
      • (3)计数器值-1
    • 4. 实验

  

一、知识点

在这里插入图片描述

1. 设备模型

  Linux 支持世界上几乎所有的,不同功能的硬件。所以Linux驱动一定要跨平台。而且现在支持的硬件数量在一直增加,代码的复杂程度也在上升。为了做好设备驱动的管理,并降低驱动开发难度。兼容设备的热拔插和电源管理。Linux对硬件设备进行了分类和归纳,并抽象出来了一套标准的数据结构和接口。这个就是设备模型。
   Linux引入了设备驱动模型分层的概念, 将我们编写的驱动代码分成了两块:设备与驱动。设备负责提供硬件资源而驱动代码负责去使用这些设备提供的硬件资源。 并由总线将它们联系起来。

在这里插入图片描述
   设备模型通过几个数据结构来反映当前系统中总线、设备以及驱动的工作状况,提出了以下几个重要概念:
  ①设备(device) :挂载在某个总线的物理设备;
  ②驱动(driver) :与特定设备相关的软件,负责初始化该设备以及提供一些操作该设备的操作方式;
  ③总线(bus) :负责管理挂载对应总线的设备以及驱动;
  ④类(class) :对于具有相同功能的设备,归结到一种类别,进行分类管理;

2. sysfs 文件系统

   sysfs文件系统是 Linux2.6版本引入的虚拟文件系统。sysfs把连接在系统上的设备模型组织成为一个分级的层次视图。并且可以向用户空间导出内核据结构以及属性。

   和设备模型有关的文件夹有classdevicesbus这三个文件夹。
在这里插入图片描述

(1)bus文件夹:这个文件夹下的所有目录是 Linux系统支持并且已经注册的总线。从总线这个角度展示现在有哪些总线以及总线下连接了什么设备和驱动。
在这里插入图片描述

(2)devices文件夹:该文件夹下的所有目录是连接到总线的全部设备,从设备级联角度进行展示。
在这里插入图片描述

(3)class文件夹:对设备进行归类。对于具有相同功能的设备,归结到一种类别。类下的所有设备都是/sys/devices下的设备的软连接。
在这里插入图片描述

3. kobject、kset设备模型框架

在这里插入图片描述

   kobject是一个面向对象的管理机制,是构成设备上述设备模型的核心结构,在内核中注册一个kobject,对应就是在sysfs文件系统中创建一个目录和目录里的一个文件夹。即一个kobject对应/sys下一个目录。
  在实际使用 kobject 的时候,一般不会单独使用。通常要嵌入到一个数据结构中。这样就可以把高级对象接入到设备模型里面。举例如下:

struct device {
	struct device		*parent;
	struct device_private	*p;
	struct kobject kobj;  //将kobj 内嵌到device设备下。
	const char		*init_name; /* initial name of the device */
	const struct device_type *type;
	...
}

//平台总线
struct platform_device {
	const char	*name;
	int		id;
	bool		id_auto;
	struct device	dev;        //包含device结构体。
	u32		num_resources;
	struct resource	*resource;
	const struct platform_device_id	*id_entry;
	/* MFD cell pointer */
	struct mfd_cell *mfd_cell;
	/* arch specific additions */
	struct pdev_archdata	archdata;
};

二、kobject实验

kobject 结构体如下,头路径:#include <linux/kobject.h>

struct kobject {
   char      * k_name;
   char      name[KOBJ_NAME_LEN];
   struct kref    kref;
   struct list_head  entry;
   struct kobject    * parent;
   struct kset    * kset;
   struct kobj_type  * ktype;
   struct dentry     * dentry;
};

1. 创建kobject

方式一:

struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)
/*
返回值:struct kobject * ,接收kobject。
参数:
	const char *name:创建的kobject的文件夹的名称。
	struct kobject *parent:创建的kobject 文件夹的父目录。
*/

方式二:

// 1. 先使用kzalloc给kobject申请一个地址空间。
struct kobject *mykoject;
struct kobj_type *myktype;
mykoject =kzalloc(sizeof(struct kobject), GFP_KERNEL);

// 2. 使用kobject_init_and_add初始化并添加一个kobject。
int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,
			 struct kobject *parent, const char *fmt, ...)
/*
		struct kobject *kobj: kobject结构体。
		struct kobj_type *ktyp:类型。
		struct kobject *parent:创建的kobject 文件夹的父目录。
		const char *fmt:名称。
使用示例:ret=kobject_init_and_add(mykoject, &myktype ,NULL, "mykojecte");
*/

2. 释放kobject

注意:在释放kobj时,要先释放子目录的kobj,在释放其父目录的kobj!

void kobject_put(struct kobject *kobj);

★示例

#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/slab.h>  // For kzalloc

struct kobject *mykobject01;
struct kobject *mykobject02;
struct kobject *mykobject03;
struct kobj_type mytype;

static int __init mykobj_init(void)
{
    int ret;

//使用方式一创建kobject。
    mykobject01 = kobject_create_and_add("mykobject01", NULL);
    if (!mykobject01)
        return -ENOMEM;

    mykobject02 = kobject_create_and_add("mykobject02", mykobject01);
    if (!mykobject02) {
        kobject_put(mykobject01);
        return -ENOMEM;
    }

//使用方式二创建kobject。
    mykobject03 = kzalloc(sizeof(struct kobject), GFP_KERNEL);
    if (!mykobject03) {
        kobject_put(mykobject02);
        kobject_put(mykobject01);
        return -ENOMEM;
    }
    
    ret = kobject_init_and_add(mykobject03, &mytype, NULL, "mykobject03");
    if (ret) {
        kobject_put(mykobject03);
        kobject_put(mykobject02);
        kobject_put(mykobject01);
        return ret;
    }
    return 0;
}

static void __exit mykobj_exit(void)
{
    kobject_put(mykobject03);
    kobject_put(mykobject02); //先释放子目录
    kobject_put(mykobject01); //再释放父目录
}

module_init(mykobj_init);
module_exit(mykobj_exit);

MODULE_LICENSE("GPL");

在这里插入图片描述

三、kset实验

   当多个kobject属于同一类的时候,为了方便管理,就引入了Kset。Kset可以认为是一组kobject的集合,是kobject的容器。比如/sys/bus下就属于同一类kobject。kset结构体如下,头文件路径:#include <linux/kobject.h>

struct kset {
   struct subsystem  * subsys;
   struct kobj_type  * ktype;
   struct list_head  list;
   struct kobject    kobj;
   struct kset_hotplug_ops  * hotplug_ops;
};

1. 创建kset

struct kset *kset_create_and_add(const char *name,
				 const struct kset_uevent_ops *uevent_ops,
				 struct kobject *parent_kobj)
/*
const char *name:kest文件夹的名字
const struct kset_uevent_ops *uevent_ops:指向 kset_uevent_ops 结构的指针,该结构包含 kset 的用户空间事件操作。常为NULL。
struct kobject *parent_kobj :父节点目录
*/

2. 注销kset

void kset_unregister(struct kset *k);

★示例

#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/slab.h>  // For kzalloc

struct kobject *mykobject01;
struct kobject *mykobject02;
struct kset *my_kset;
struct kobj_type mytype;

static int __init mykobj_init(void)
{
    int ret;
    //1. 创建kest
    my_kset=kset_create_and_add("my_kset",NULL,NULL); //在sys下创建my_kset目录.
    //2. 创建kobject1
    mykobject01= kzalloc(sizeof(struct kobject), GFP_KERNEL); 
    mykobject01->kset=my_kset;
    ret = kobject_init_and_add(mykobject01, &mytype, NULL, "mykobject01");
    //3. 创建kobject2
    mykobject02= kzalloc(sizeof(struct kobject), GFP_KERNEL); 
    mykobject02->kset=my_kset;
    ret = kobject_init_and_add(mykobject02, &mytype, NULL, "mykobject02");
  
    return 0;
}

static void __exit mykobj_exit(void)
{
    kobject_put(mykobject01);
    kobject_put(mykobject02);
    kset_unregister(my_kset);
}

module_init(mykobj_init);
module_exit(mykobj_exit);

MODULE_LICENSE("GPL");

在这里插入图片描述

四、引用计数器

1. 概念

  引用计数器是一个计数器,它记录了某个资源当前被多少个用户或对象引用。当资源被引用时,引用计数器增加;当引用被释放时,引用计数器减少。当引用计数器减少到零时,表示资源不再被任何对象引用,此时可以安全地释放资源。

2. 为什么要引入引用计数器?

  答:如果我们写了一个字符驱动。当硬件设备插上时,系统会生成一个设备节点。用户在应用空间操作这个设备节点就可以操作设备。如果此时将硬件断开。驱动是不是就要立刻释放呢?如果立刻释放,应用程序是不是就崩了呢。所以要等应用程序关闭,在去释放驱动。要如何实现呢?在 Linux 系统中是通过引用技术去来实现的。比如用 kref这个变量记录某个驱动或者某块内存的引用次数。初始值为1,每引用一次+1,每取消引用一次-1,当计数值为0的时候。自动调用自定义的释放函数进行释放驱动或者内存。

3. 常用函数

引用计数器使用结构体struct kref来描述,结构体如下。头文件路径:#include <linux/kref.h>

struct kref {
	atomic_t refcount;
};

  在使用引用计数器时,结构体kref一般被嵌入进其他结构中来使用。如结构体 kobject。当然也可以在自定义的结构体中使用该结构体。以下是对原子操作的进一步封装的函数。

(1)初始化计数器值为1

void kref_init(struct kref *kref);

(2)计数器值+1

void kref_get(struct kref *kref);

(3)计数器值-1

  当计数器的值为0时,会自动调用自定义的release函数执行释放操作。kobject_put是对该函数的进一步封装。

int kref_put(struct kref *kref, void (*release)(struct kref *kref));
//void (*release)(struct kref *kref):自定义的release函数。

4. 实验

  现象:当我们创建一个kobj时,该kobj里的引用计数器就会自动进行+1操作(初值为0)。当我们卸载一个kobj时,该kobj里的引用计数器就会自动进行-1操作,这是因为kobj的创建和卸载函数是对引用计数器操作函数的进一步封装,实质还是对计数器的操作。

#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/kernel.h>
#include <linux/slab.h>  // For kzalloc
#include <linux/kref.h>
#include <linux/atomic.h>

struct kobject *mykoject01;
struct kobject *mykoject02;
struct kobject *mykoject03;
struct kobj_type mytype;

static int __init mykobj_init(void)
{
    int ret;

    mykoject01 = kobject_create_and_add("mykoject01", NULL);
	printk("mykoject01 kref is %d\n", atomic_read(&mykoject01->kref.refcount));

    mykoject02 = kobject_create_and_add("mykoject02", mykoject01);
	printk("mykoject01 kref is %d\n", atomic_read(&mykoject01->kref.refcount));

    mykoject03 = kzalloc(sizeof(struct kobject), GFP_KERNEL);
    ret = kobject_init_and_add(mykoject03, &mytype, NULL, "mykoject03");
	printk("mykoject03 kref is %d\n", atomic_read(&mykoject03->kref.refcount));

    return 0;
}

static void __exit mykobj_exit(void)
{
	printk("mykoject01 kref is %d\n", atomic_read(&mykoject01->kref.refcount));
    printk("mykoject02 kref is %d\n", atomic_read(&mykoject02->kref.refcount));
    printk("mykoject03 kref is %d\n", atomic_read(&mykoject03->kref.refcount));
    
    kobject_put(mykoject01);
	printk("mykoject01 kref is %d\n", atomic_read(&mykoject01->kref.refcount));
    printk("mykoject02 kref is %d\n", atomic_read(&mykoject02->kref.refcount));
    printk("mykoject03 kref is %d\n", atomic_read(&mykoject03->kref.refcount));

    kobject_put(mykoject02);
	printk("mykoject01 kref is %d\n", atomic_read(&mykoject01->kref.refcount));
    printk("mykoject02 kref is %d\n", atomic_read(&mykoject02->kref.refcount));
    printk("mykoject03 kref is %d\n", atomic_read(&mykoject03->kref.refcount));

    kobject_put(mykoject03);
	printk("mykoject01 kref is %d\n", atomic_read(&mykoject01->kref.refcount));
    printk("mykoject02 kref is %d\n", atomic_read(&mykoject02->kref.refcount));
    printk("mykoject03 kref is %d\n", atomic_read(&mykoject03->kref.refcount));
}

module_init(mykobj_init);
module_exit(mykobj_exit);

MODULE_LICENSE("GPL");

现象:当kobj2卸载时,kobj1的计数器也会-1。
在这里插入图片描述

理解
在这里插入图片描述

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

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

相关文章

【Nuxt】发送请求

概述 以下方式只能在 setup / 生命周期钩子 里面使用。 useFetch 下面的 API / hooks 具体用法查看官网文档。 const BASE_URL http://codercba.com:9060/juanpi/api;// 1. $fetch server and client // $fetch(BASE_URL /homeInfo, { // method: GET // }).then(res &…

python爬虫04 | Reuqests库快速入门,干穿urllib

文章目录 Requests库简介提出请求响应内容二进制响应内容JSON 响应内容原始响应内容自定义标头更复杂的 POST 请求POST 多部分编码的文件响应状态代码响应标头Cookie重定向和历史记录超时错误和异常 Ending Requests库简介 什么是Requests库 Requests是一个简单易用的HTTP库&…

分享一个基于SpringBoot和Vue的闲置物品交易与物品租赁平台(源码、调试、LW、开题、PPT)

&#x1f495;&#x1f495;作者&#xff1a;计算机源码社 &#x1f495;&#x1f495;个人简介&#xff1a;本人 八年开发经验&#xff0c;擅长Java、Python、PHP、.NET、Node.js、Android、微信小程序、爬虫、大数据、机器学习等&#xff0c;大家有这一块的问题可以一起交流&…

人工智能计算机视觉先锋——OpenCv 的颜色检测

红色 在计算机的世界里&#xff0c;只有 0 或者1&#xff0c;如何让计算机认识颜色是计算机视觉工作者首先需要考虑的事情&#xff0c;我们知道整个世界的颜色虽然五彩缤纷&#xff0c;但是都是3种原色彩合成的&#xff08;R G B&#xff09;&#xff0c;有了&#xff08;R G …

C语言 | Leetcode C语言题解之第327题区间和的个数

题目&#xff1a; 题解&#xff1a; #define FATHER_NODE(i) (0 (i) ? -1 : ((i) - 1 >> 1)) #define LEFT_NODE(i) (((i) << 1) 1) #define RIGHT_NODE(i) (((i) << 1) 2)/* 优先队列&#xff08;小根堆&#xff09;。 */ typedef s…

数字人解决方案——音频驱动机器人

音频集成 机器人 标志着 人工智能&#xff08;AI&#xff09;。 想象一下&#xff0c;机器人可以通过视觉和听觉导航并与周围环境互动。音频驱动的机器人使这成为可能&#xff0c;提高了它们更高效、更直观地执行任务的能力。这一发展可能会影响到各个领域&#xff0c;包括家庭…

解决PermissionError: [Errno 13] Permission denied: “xx“报错

这个报错我是使用 shutil.copy(src_file, dst_file) 语句产生的&#xff0c;因此有些问题我会围绕此句代码来进行解决&#xff0c;如果有更好的建议&#xff0c;欢迎积极留言。 目录 1.路径拼写错误&#xff0c;建议使用绝对路径 2.此文件正在使用&#xff0c;关闭当前打开的…

vba 保存word里面的图片_1分钟批量处理100张图片,有Word在

天下苦Word久矣&#xff01;Word不仅是个码字工具&#xff0c;还是个排版工具&#xff0c;而Word在排版方面经常遇到的问题&#xff0c;恐怕说个三天三夜都说不完&#xff01; 好不容易做完了100页的活动方案&#xff0c;交到处女座上司那里&#xff0c;他告诉我&#xff1a;“…

调用azure的npm实现outlook_api模拟查看邮件、发送邮件(实现web版接受outlook邮件第一步)

文章目录 ⭐前言⭐注册azure应用&#x1f496;添加权限 ⭐调用npm 实现收发邮件&#x1f496;安装依赖&#x1f496;创建appSettings.js 放置密钥&#x1f496;创建graphHelper.js封装功能&#x1f496;主文件index.js 对外暴露&#x1f496;效果 ⭐结束 ⭐前言 大家好&#x…

我的cesium for UE踩坑之旅(蓝图、UI创建)

我的小小历程 过程创建对应目录&#xff0c;并将要用到的图片、资源放入对应目录下内容浏览器 窗口中右键&#xff0c;创建一个控件蓝图&#xff0c;用来编辑界面UI绘制画布面板&#xff08;canvas&#xff09;调整整体布局加入对应的控件将UI加入到关卡中 备注搜索不到 Add To…

【原创】简易学生成绩查询系统Excel版

简易学生成绩查询系统通常是为了方便学校、教师和学生能够快速查询和管理成绩而设计的一种工具。从之前提供的信息来看&#xff0c;我们可以总结出简易学生成绩查询系统的一些常见功能&#xff1a; ### 易查分成绩查询系统功能特点&#xff1a; - **成绩导入与管理**&#xff…

Spark_获取id对应日期的所在月份的天数完整指南

开发背景 前段时间有一个开发需求的一小块用到了这&#xff0c;是一个利用率的计算。规则是某id下的近半年的值的小时利用率。 计算规则是某值除以近半年 天数以及24h,但是月份里面数据有空值&#xff0c;所以要计算一下id对应的月份的天数&#xff0c;并且过滤掉数据有空值的天…

Azure openai connection with javascript

题意&#xff1a;使用JavaScript与Azure OpenAI进行连接 问题背景&#xff1a; I have created my chatbot with javascript and used open ai. I need to change it to azure open ai but can not find the connection details for javascript. This is how i connect with p…

十九、虚拟机VMware Workstation(CentOSDebian)的安装

目录 &#x1f33b;&#x1f33b; 一、安装 VMware Workstation1.1 安装 VMware Workstation1.2 虚拟机上安装 CentOS1.3 虚拟机安装 Debian 二、配置Debian方便第三方工具远程连接2.1 配置debian2.2 安装远程SSH工具并连接 一、安装 VMware Workstation 官网下载 本地资源库…

端到端自动驾驶:终局还是误区?

近年来&#xff0c;端到端自动驾驶技术成为了汽车行业的热议话题。尤其是在2024年&#xff0c;各家新兴车企纷纷打出端到端的旗号&#xff0c;似乎谁没有搞端到端&#xff0c;就会被市场淘汰。然而&#xff0c;端到端自动驾驶真的是自动驾驶技术的终局吗&#xff1f;本文将深入…

使用QML的ListView自制树形结构图TreeView

背景 感觉QML自带的TreeView不是很好用&#xff0c;用在文件路径树形结构比较多&#xff0c;但是想用在自己数据里&#xff0c;就不太方便了&#xff0c;所以自己做一个。 用‘ListView里迭代ListView’的方法&#xff0c;制作树形结构&#xff0c;成果图&#xff1a; 代码…

尚硅谷谷粒商城项目笔记——四、使用docker安装redis【电脑CPU:AMD】

四、使用docker安装redis 注意&#xff1a; 因为电脑是AMD芯片&#xff0c;自己知识储备不够&#xff0c;无法保证和课程中用到的环境一样&#xff0c;所以环境都是自己根据适应硬件软件环境重新配置的&#xff0c;这里的虚拟机使用的是VMware。 在解决了 Docker 安装的问题之…

app逆向抓包技巧:SSL Pinning检测绕过

本篇博客旨在记录学习过程&#xff0c;不可用于商用等其它途径 场景 在charles抓包下&#xff0c;某斑马app在注册时发现点击登录毫无反应&#xff0c;看抓包结果提示SSL handshake with client failed&#xff0c;确定是触发了SSL/TLS Pinning&#xff08;证书锁定&#xff…

Flutter 正在迁移到 Swift Package Manager ,未来会弃用 CocoaPods 吗?

什么是 Swift Package Manager &#xff1f;其实 Swift Package Manager (SwiftPM) 出现已经挺长一段时间了&#xff0c;我记得第一次听说 SwiftPM 的时候&#xff0c;应该还是在 2016 年&#xff0c;那时候 Swift 3 刚发布&#xff0c;不过正式出场应该还是在 2018 年的 Apple…

【研发日记】嵌入式处理器技能解锁(二)——TI C2000 DSP的SCI(串口)通信

文章目录 前言 背景介绍 SCI通信 Transmitter Receiver SCI中断 分析和应用 总结 参考资料 前言 见《【研发日记】嵌入式处理器技能解锁(一)——多任务异步执行调度的三种方法》 背景介绍 近期使用TI C2000 DSP做的一个嵌入式系统开发项目中&#xff0c;在使用它的SCI&…