Linux设备模型(二) - kset/kobj/ktype APIs

一,kobject_init_and_add

1,kobject_init_and_add实现

/**
* kobject_init_and_add() - Initialize a kobject structure and add it to
*                          the kobject hierarchy.
* @kobj: pointer to the kobject to initialize
* @ktype: pointer to the ktype for this kobject.
* @parent: pointer to the parent of this kobject.
* @fmt: the name of the kobject.
*
* This function combines the call to kobject_init() and kobject_add().
*
* If this function returns an error, kobject_put() must be called to
* properly clean up the memory associated with the object.  This is the
* same type of error handling after a call to kobject_add() and kobject
* lifetime rules are the same here.
*/
int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,struct kobject *parent, const char *fmt, ...)
{va_list args;int retval;kobject_init(kobj, ktype);va_start(args, fmt);retval = kobject_add_varg(kobj, parent, fmt, args);va_end(args);return retval;
}

2,函数调用流程

error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL, "%s", drv->name);
----kobject_init(kobj, ktype);
--------kobject_init_internal(kobj);
------------kref_init(&kobj->kref);
------------kobj->state_initialized = 1;
----kobject_add_varg(kobj, parent, fmt, args);
--------kobject_set_name_vargs(kobj, fmt, vargs);
--------kobject_add_internal(kobj);
------------parent = kobject_get(kobj->parent);
------------if (kobj->kset)
------------kobj_kset_join(kobj);
----------------list_add_tail(&kobj->entry, &kobj->kset->list);
------------create_dir(kobj);
----------------sysfs_create_dir_ns(kobj, kobject_namespace(kobj));
----------------populate_dir(kobj);
----------------sysfs_create_groups(kobj, ktype->default_groups);

二,kobject_create_and_add

1,kobject_create_and_add实现

/**
* kobject_create_and_add() - Create a struct kobject dynamically and
*                            register it with sysfs.
* @name: the name for the kobject
* @parent: the parent kobject of this kobject, if any.
*
* This function creates a kobject structure dynamically and registers it
* with sysfs.  When you are finished with this structure, call
* kobject_put() and the structure will be dynamically freed when
* it is no longer being used.
*
* If the kobject was not able to be created, NULL will be returned.
*/
struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)
{struct kobject *kobj;int retval;kobj = kobject_create();if (!kobj)return NULL;retval = kobject_add(kobj, parent, "%s", name);if (retval) {pr_warn("%s: kobject_add error: %d\n", __func__, retval);kobject_put(kobj);kobj = NULL;}return kobj;
}
EXPORT_SYMBOL_GPL(kobject_create_and_add);

2,函数调用流程

fw_ctrl->kobj = kobject_create_and_add("fwupdate", &core_data->pdev->dev.kobj);
----kobj = kobject_create();
--------kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
--------kobject_init(kobj, &dynamic_kobj_ktype);
----kobject_add(kobj, parent, "%s", name);
--------if (!kobj->state_initialized)
--------kobject_add_varg(kobj, parent, fmt, args);//The main kobject add function

3,kobject_add_internal

static int kobject_add_internal(struct kobject *kobj)
{int error = 0;struct kobject *parent;if (!kobj)return -ENOENT;//kbj的名字不能为空if (!kobj->name || !kobj->name[0]) {WARN(1,"kobject: (%p): attempted to be registered with empty name!\n",kobj);return -EINVAL;}//增加kobj->parent的引用计数kref+1parent = kobject_get(kobj->parent);//如果kobj属于某个kset但是该kobj的parent为空,将kset->kobj作为作为该kobj的parent/* join kset if set, use it as parent if we do not already have one */if (kobj->kset) {if (!parent)parent = kobject_get(&kobj->kset->kobj);/* add the kobject to its kset's list */kobj_kset_join(kobj);kobj->parent = parent;}//打印kobj的名字和kobj parent的名字pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n",kobject_name(kobj), kobj, __func__,parent ? kobject_name(parent) : "<NULL>",kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");//创建名字为kobject_name(kobj)的目录,使用kobj_type->default_attrs[i]和kobj_type->default_groups在dir中创建文件节点error = create_dir(kobj);if (error) {kobj_kset_leave(kobj);kobject_put(parent);kobj->parent = NULL;/* be noisy on error issues */if (error == -EEXIST)pr_err("%s failed for %s with -EEXIST, don't try to register things with the same name in the same directory.\n",__func__, kobject_name(kobj));elsepr_err("%s failed for %s (error: %d parent: %s)\n",__func__, kobject_name(kobj), error,parent ? kobject_name(parent) : "'none'");} else//kobj已经在sysfs中创建了dir和node,设置flagkobj->state_in_sysfs = 1;return error;
}
/* add the kobject to its kset's list */
static void kobj_kset_join(struct kobject *kobj)
{if (!kobj->kset)return;kset_get(kobj->kset);spin_lock(&kobj->kset->list_lock);//所有属于该kset的kobj都会挂在kset->list链表上list_add_tail(&kobj->entry, &kobj->kset->list);spin_unlock(&kobj->kset->list_lock);
}

4,kobject_create_and_add的一种使用

有一种例外,Kobject不再嵌在其它数据结构中,可以单独使用,这个例外就是:开发者只需要在sysfs中创建一个目录,而不需要其它的kset、ktype的操作。这时可以直接调用kobject_create_and_add接口,分配一个kobject结构并把它添加到kernel中。

例如在sysfs device的目录中创建一个文件夹然后在其中创建文件节点:

static int fw_sysfs_init(struct ts_core *core_data,struct fw_update_ctrl *fw_ctrl)
{int ret = 0, i;fw_ctrl->kobj = kobject_create_and_add("fwupdate",&core_data->pdev->dev.kobj);if (!fw_ctrl->kobj) {                                              ts_err("failed create sub dir for fwupdate");return -EINVAL;}for (i = 0; i < ARRAY_SIZE(fwu_attrs) && !ret; i++)ret = sysfs_create_file(fw_ctrl->kobj, fwu_attrs[i]);if (ret) {ts_err("failed create fwu sysfs files");while (--i >= 0)sysfs_remove_file(fw_ctrl->kobj, fwu_attrs[i]);kobject_put(fw_ctrl->kobj);return -EINVAL;}return ret;
}

三,Kobject引用计数的修改

通过kobject_get和kobject_put可以修改kobject的引用计数,并在计数为0时,调用ktype的release接口,释放占用空间。

1: /* include/linux/kobject.h, line 103 */
2: extern struct kobject *kobject_get(struct kobject *kobj);
3: extern void kobject_put(struct kobject *kobj);
kobject_get,调用kref_get,增加引用计数。
kobject_put,以内部接口kobject_release为参数,调用kref_put。kref模块会在引用计数为零时,调用kobject_release。
==========================内部接口======================================
kobject_release,通过kref结构,获取kobject指针,并调用kobject_cleanup接口继续。
kobject_cleanup,负责释放kobject占用的空间,主要执行逻辑如下:
* 检查该kobject是否有ktype,如果没有,打印警告信息
* 如果该kobject向用户空间发送了ADD uevent但没有发送REMOVE uevent,补发REMOVE uevent
* 如果该kobject有在sysfs文件系统注册,调用kobject_del接口,删除它在sysfs中的注册
* 调用该kobject的ktype的release接口,释放内存空间
* 释放该kobject的name所占用的内存空间

四,kset_create_and_add

1,kset_create_and_add实现

/**
* kset_create_and_add() - Create a struct kset dynamically and add it to sysfs.
*
* @name: the name for the kset
* @uevent_ops: a struct kset_uevent_ops for the kset
* @parent_kobj: the parent kobject of this kset, if any.
*
* This function creates a kset structure dynamically and registers it
* with sysfs.  When you are finished with this structure, call
* kset_unregister() and the structure will be dynamically freed when it
* is no longer being used.
*
* If the kset was not able to be created, NULL will be returned.
*/
struct kset *kset_create_and_add(const char *name,const struct kset_uevent_ops *uevent_ops,struct kobject *parent_kobj)
{struct kset *kset;int error;kset = kset_create(name, uevent_ops, parent_kobj);if (!kset)return NULL;error = kset_register(kset);if (error) {kfree(kset);return NULL;}return kset;
}
EXPORT_SYMBOL_GPL(kset_create_and_add);

2,函数调用流程

bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
----kset = kset_create(name, uevent_ops, parent_kobj);
--------kset = kzalloc(sizeof(*kset), GFP_KERNEL);
--------retval = kobject_set_name(&kset->kobj, "%s", name);
--------kset->kobj.ktype = &kset_ktype;
----kset_register(kset);
--------kset_init(k);
------------kobject_init_internal(&k->kobj);
----------------kobj->state_initialized = 1;
--------kobject_add_internal(&k->kobj);
--------kobject_uevent(&k->kobj, KOBJ_ADD);
------------kobject_uevent_env(kobj, action, NULL);
----------------if (uevent_ops && uevent_ops->filter)
----------------kobject_uevent_net_broadcast(kobj, env, action_string, devpath);
--------------------uevent_net_broadcast_untagged(env, action_string, devpath);
------------------------skb = alloc_uevent_skb(env, action_string, devpath);
----------------------------skb_put_data(skb, env->buf, env->buflen);
------------------------netlink_broadcast(uevent_sock, skb_get(skb), 0, 1, GFP_KERNEL);
----------------------------netlink_broadcast_filtered(ssk, skb, portid, group, allocation, NULL, NULL);

使用示例可以参考下一节的“kset/kobj/ktype使用示例"。

五,总结,Ktype以及整个Kobject机制的理解

Kobject的核心功能是:保持一个引用计数,当该计数减为0时,自动释放(由本文所讲的kobject模块负责) Kobject所占用的meomry空间。这就决定了Kobject必须是动态分配的(只有这样才能动态释放)。

而Kobject大多数的使用场景,是内嵌在大型的数据结构中(如Kset、device_driver等),因此这些大型的数据结构,也必须是动态分配、动态释放的。那么释放的时机是什么呢?是内嵌的Kobject释放时。但是Kobject的释放是由Kobject模块自动完成的(在引用计数为0时),那么怎么一并释放包含自己的大型数据结构呢?

这时Ktype就派上用场了。我们知道,Ktype中的release回调函数负责释放Kobject(甚至是包含Kobject的数据结构)的内存空间,那么Ktype及其内部函数,是由谁实现呢?是由上层数据结构所在的模块!因为只有它,才清楚Kobject嵌在哪个数据结构中,并通过Kobject指针以及自身的数据结构类型,找到需要释放的上层数据结构的指针,然后释放它。

讲到这里,就清晰多了。所以,每一个内嵌Kobject的数据结构,例如kset、device、device_driver等等,都要实现一个Ktype,并定义其中的回调函数。同理,sysfs相关的操作也一样,必须经过ktype的中转,因为sysfs看到的是Kobject,而真正的文件操作的主体,是内嵌Kobject的上层数据结构!

顺便提一下,Kobject是面向对象的思想在Linux kernel中的极致体现,但C语言的优势却不在这里,所以Linux kernel需要用比较巧妙(也很啰嗦)的手段去实现。

1,kset_ktype

//定义
static struct kobj_type kset_ktype = {.sysfs_ops    = &kobj_sysfs_ops,.release    = kset_release,.get_ownership    = kset_get_ownership,
};
static void kset_release(struct kobject *kobj)
{struct kset *kset = container_of(kobj, struct kset, kobj);pr_debug("kobject: '%s' (%p): %s\n",kobject_name(kobj), kobj, __func__);kfree(kset);
}//使用
static struct kset *kset_create(const char *name,const struct kset_uevent_ops *uevent_ops,struct kobject *parent_kobj)
{struct kset *kset;int retval;kset = kzalloc(sizeof(*kset), GFP_KERNEL);if (!kset)return NULL;retval = kobject_set_name(&kset->kobj, "%s", name);if (retval) {kfree(kset);return NULL;}kset->uevent_ops = uevent_ops;kset->kobj.parent = parent_kobj;/** The kobject of this kset will have a type of kset_ktype and belong to* no kset itself.  That way we can properly free it when it is* finished being used.*/kset->kobj.ktype = &kset_ktype;kset->kobj.kset = NULL;return kset;
}

2,bus_ktype

//定义
static struct kobj_type bus_ktype = {.sysfs_ops    = &bus_sysfs_ops,.release    = bus_release,
};static void bus_release(struct kobject *kobj)
{struct subsys_private *priv = to_subsys_private(kobj);struct bus_type *bus = priv->bus;kfree(priv);bus->p = NULL;
}static const struct sysfs_ops bus_sysfs_ops = {.show    = bus_attr_show,.store    = bus_attr_store,
};//使用
int bus_register(struct bus_type *bus)
{int retval;struct subsys_private *priv;struct lock_class_key *key = &bus->lock_key;priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL);if (!priv)return -ENOMEM;priv->bus = bus;bus->p = priv;BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);if (retval)goto out;priv->subsys.kobj.kset = bus_kset;priv->subsys.kobj.ktype = &bus_ktype;priv->drivers_autoprobe = 1;... ...
}

3,device_ktype

//定义
static struct kobj_type device_ktype = {.release    = device_release,.sysfs_ops    = &dev_sysfs_ops,.namespace    = device_namespace,.get_ownership    = device_get_ownership,
};
static void device_release(struct kobject *kobj)
{struct device *dev = kobj_to_dev(kobj);struct device_private *p = dev->p;/** Some platform devices are driven without driver attached* and managed resources may have been acquired.  Make sure* all resources are released.** Drivers still can add resources into device after device* is deleted but alive, so release devres here to avoid* possible memory leak.*/devres_release_all(dev);kfree(dev->dma_range_map);if (dev->release)dev->release(dev);else if (dev->type && dev->type->release)dev->type->release(dev);else if (dev->class && dev->class->dev_release)dev->class->dev_release(dev);elseWARN(1, KERN_ERR "Device '%s' does not have a release() function, it is broken and must be fixed. See Documentation/core-api/kobject.rst.\n",dev_name(dev));kfree(p);
}//使用
void device_initialize(struct device *dev)
{dev->kobj.kset = devices_kset;kobject_init(&dev->kobj, &device_ktype);INIT_LIST_HEAD(&dev->dma_pools);mutex_init(&dev->mutex);
#ifdef CONFIG_PROVE_LOCKINGmutex_init(&dev->lockdep_mutex);
#endif... ...
}

4,driver_ktype

//定义
static struct kobj_type driver_ktype = {.sysfs_ops    = &driver_sysfs_ops,.release    = driver_release,
};
static void driver_release(struct kobject *kobj)
{struct driver_private *drv_priv = to_driver(kobj);pr_debug("driver: '%s': %s\n", kobject_name(kobj), __func__);kfree(drv_priv);
}//使用
int bus_add_driver(struct device_driver *drv)
{struct bus_type *bus;struct driver_private *priv;int error = 0;bus = bus_get(drv->bus);if (!bus)return -EINVAL;pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);priv = kzalloc(sizeof(*priv), GFP_KERNEL);if (!priv) {error = -ENOMEM;goto out_put_bus;}klist_init(&priv->klist_devices, NULL, NULL);priv->driver = drv;drv->p = priv;priv->kobj.kset = bus->p->drivers_kset;error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,"%s", drv->name);if (error)goto out_unregister;... ...
}

参考:

Linux设备模型(2)_Kobject

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

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

相关文章

探索无限:Sora与AI视频模型的技术革命 - 开创未来视觉艺术的新篇章

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua&#xff0c;在这里我会分享我的知识和经验。&#x…

Unity实现帧序列

一、目的 1.想实现序列帧效果 自己使用Animation一直无法实现动画播放效果 二、参考 1. Unity序列帧动画——Sprite图片集制作UI动画_unity 序列帧动画图集-CSDN博客 结果&#xff1a;很好用&#xff0c;能实现效果 三、实操 新建Image&#xff0c;增加Animator组件&#x…

【数据结构】排序(1)

目录 一、概念&#xff1a; 二、直接插入排序&#xff1a; 三、希尔排序&#xff1a; 四、直接选择排序&#xff1a; 五、堆排序&#xff1a; 六、冒泡排序&#xff1a; 一、概念&#xff1a; 排序的概念&#xff1a; 使一串记录&#xff0c;按照其中的某个或某些关键字…

【电路笔记】-RC放电电路

RC放电电路 文章目录 RC放电电路1、概述2、RC放电电路3、RC放电电路示例当电压源从完全充电的 RC 电路中移除时,电容器 C 将通过电阻 R 放电。 1、概述 RC 放电电路利用电阻器-电容器组合的固有 RC 时间常数以指数衰减率对电容器进行放电。 在之前的 RC 充电电路教程中,我们…

向量的组成

向量是有序的一维数组&#xff0c;其中包含多个标量元素。每个元素都有一个索引&#xff0c;表示其在向量中的位置。在数学表示中&#xff0c;我们通常用小写粗体字母表示向量&#xff0c;如 v。 关于向量、张量、标量与矩阵的关系可以参见另一篇文章&#xff1a;线性代数&…

开源免费的NTFS for mac工具mounty

开源免费的NTFS for mac工具mounty 安装依赖 brew install gromgit/fuse/ntfs-3g-macbrew install --cask macfuse安装mounty 如果已经安装macFUSE和ntfs-3g-mac&#xff0c;可以直接点击下载的dmg安装包&#xff0c;安装升级。第一次启动mounty&#xff0c;你需要接受一系列…

项目优化-

前言 用户浏览菜品&#xff0c;添加购物车&#xff0c;下单等操作最终都会反映成一个sql&#xff0c;操作数据库。 但是当前系统只部署了一台数据库&#xff0c;读和写所有压力都由一台数据库承担&#xff0c;压力大&#xff1b;如果数据库服务器磁盘损坏则数据丢失&#xff0…

【这个词(Sequence-to-Sequence)在深度学习中怎么解释,有什么作用?】

&#x1f680; 作者 &#xff1a;“码上有前” &#x1f680; 文章简介 &#xff1a;深度学习笔记 &#x1f680; 欢迎小伙伴们 点赞&#x1f44d;、收藏⭐、留言&#x1f4ac; Sequence-to-Sequence&#xff08;Seq2Seq&#xff09; Sequence-to-Sequence&#xff08;Seq2Seq…

【论文阅读】ICCV 2023 计算和数据高效后门攻击

文章目录 一.论文信息二.论文内容1.摘要2.引言3.主要图表4.结论 一.论文信息 论文题目&#xff1a; Computation and Data Efficient Backdoor Attacks&#xff08;计算和数据高效后门攻击&#xff09; 论文来源&#xff1a; 2023-ICCV&#xff08;CCF-A&#xff09; 论文团…

【DDD】学习笔记-薪资管理系统的测试驱动开发

回顾薪资管理系统的设计建模 在 3-15 课&#xff0c;我们通过场景驱动设计完成了薪资管理系统的领域设计建模。既然场景驱动设计可以很好地与测试驱动开发融合在一起&#xff0c;因此根据场景驱动设计的成果来开展测试驱动开发&#xff0c;就是一个水到渠成的过程。让我们先来…

List集合之UML、特点、遍历方式、迭代器原理、泛型、装拆箱及ArrayList、LinkedList和Vector的区别

目录 ​编辑 一、什么是UML 二、集合框架 三、List集合 1.特点 2.遍历方式 3.删除 4.优化 四、迭代器原理 五、泛型 六、装拆箱 七、ArrayList、LinkedList和Vector的区别 ArrayList和Vector的区别 LinkedList和Vector的区别 一、什么是UML UML&#xff08;Unif…

前端项目打包体积分析与优化

一、安装依赖分析工具 npm install webpack-bundle-analyz 二、修改webpack.config.js文件 1、导入上面下载的包 2、在plugins里创建实例 三、启动打包命令 npm run build 会弹出如下界面&#xff1a; 四、优化 1、通过CDN导入react-dom文件 修改webpack.config.js文件里…

Windows 远程控制 Mac 电脑怎么操作

要从 Windows 远程控制 Mac 电脑&#xff0c;您可以使用内置 macOS 功能或第三方软件解决方案。以下是一些方法&#xff1a; 一、使用内置 macOS 功能&#xff08;屏幕共享&#xff09; 1、在 macOS 上启用屏幕共享 转至系统偏好设置 > 共享&#xff1b;选中“屏幕共享”…

八、线性代数二 ,矩阵的秩

目录 1、矩阵子式的定义与子式个数的计算&#xff1a; 2、矩阵秩的定义&#xff1a; 3、矩阵秩的计算方法&#xff1a; 4、矩阵秩的 性质&#xff1a; 线性代数四——几个重要的矩阵点积_线性代数 矩阵点积-CSDN博客 1、矩阵子式的定义与子式个数的计算&#xff1a; 概念&…

C# OpenCvSharp 利用白平衡技术进行图像修复

目录 效果 灰度世界(GrayworldWB)-白平衡算法 完美反射(SimpleWB)-白平衡算法 基于学习的(LearningBasedWB)-白平衡算法 代码 下载 C# OpenCvSharp 利用白平衡技术进行图像修复 OpenCV xphoto模块中提供了三种不同的白平衡算法&#xff0c;分别是&#xff1a;灰度世界(G…

华清远见嵌入式学习——驱动开发——day9

目录 作业要求&#xff1a; 作业答案&#xff1a; 代码效果&#xff1a; ​编辑 Platform总线驱动代码&#xff1a; 应用程序代码&#xff1a; 设备树配置&#xff1a; 作业要求&#xff1a; 通过platform总线驱动框架编写LED灯的驱动&#xff0c;编写应用程序测试&…

危险!Wyze 摄像头安全漏洞致1.3万用户隐私遭窥探

最近&#xff0c;一则关于 Wyze 摄像头再次出现安全漏洞的新闻引起了人们的广泛关注。据报道&#xff0c;该安全漏洞导致约1.3万用户的摄像头受到了未经授权的访问&#xff0c;使得这些用户的隐私信息遭到了窥视。这一事件再次引发了人们对网络安全的关注和讨论。 网络安全不仅…

啊丢的刷题记录手册

1.洛谷题P1923 求第k小的数 题目描述 输入 n&#xff08;1≤n<5000000 且 n 为奇数&#xff09;个数字ai​&#xff08;1≤ai​<109&#xff09;&#xff0c;输出这些数字的第 k 小的数。最小的数是第 0 小。 请尽量不要使用 nth_element 来写本题&#xff0c;因为本题…

module ‘json‘ has no attribute ‘dumps‘

如果在使用Python的json模块时遇到AttributeError: module json has no attribute dumps错误&#xff0c;通常是因为在Python环境中json模块不支持dumps方法。这种情况可能是因为Python的json模块被重命名或修改过导致的。 解决方法可以尝试以下几种&#xff1a; 1.检查Pytho…

基于DPU和HADOS-RACE加速Spark 3.x

背景简介 Apache Spark&#xff08;下文简称Spark&#xff09;是一种开源集群计算引擎&#xff0c;支持批/流计算、SQL分析、机器学习、图计算等计算范式&#xff0c;以其强大的容错能力、可扩展性、函数式API、多语言支持&#xff08;SQL、Python、Java、Scala、R&#xff09…