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.hqwc.cn/news/486879.html

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

相关文章

Vue3 + Ts (使用lodash)

安装 npm i --save lodash使用 import _ from lodash⚠️报警告&#xff1a;&#xff01;&#xff01;&#xff01; 此时还需要安装ts声明文件库 npm install types/lodash -D安装之后重启Vscode还是会提示上面的警告&#xff0c;此时还需在tsconfig.ts里面配置 {"c…

【力扣 - 搜索插入位置】

题目描述 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 题解1 int searchInsert(int* nums, int numsSize, in…

harbor(docker仓库)仓库部署 - 高可用

harbor&#xff08;docker仓库&#xff09;仓库部署 - 高可用 1. harbor高可用1.1 方案说明1. 双主复制2. 多harbor实例共享后端存储 1.2 部署高可用&#xff08;多harbor实例共享后端存储&#xff09;1. 服务器划分2. 安装harbor&#xff08;先部署一套Harbor&#xff0c;用于…

Python代码实现2024年刘谦春晚魔术

import randomdef main():# 扑克牌随机抽取4张牌playingCards [A, 2, 3, 4, 5, 6, 7, 8, 9, 10, J, Q, K]cardTackA []for i in range(4):k random.choice(playingCards)cardTackA.append(k)# 将抽取的4张牌随机打乱cnt 0while cnt < 100:random.shuffle(cardTackA)cnt …

企业级人脸美颜和美妆解决方案

视觉营销日益重要&#xff0c;而人脸美颜和美妆作为视觉营销的关键环节&#xff0c;更是受到了众多企业的关注。美摄科技&#xff0c;作为国内领先的人脸美颜和美妆解决方案提供商&#xff0c;以其先进的技术和卓越的产品&#xff0c;助力企业打造完美视觉体验&#xff0c;提升…

快速学习安全框架 Springsecurity最新版(6.2)--用户授权模块

简介 上一节Springsecurity 用户认证 Springsecurity 拥有强大的认证和授权功能并且非常灵活&#xff0c;,一来说我们都i有以下需求 可以帮助应用程序实现以下两种常见的授权需求&#xff1a; 用户-权限-资源&#xff1a;例如张三的权限是添加用户、查看用户列表&#xff0c;李…

星宸科技SSC369G 双4K高性价比AI IPC方案

一、方案描述 SSC369G 双4K高性价比AI IPC方案采用主芯片SSC369G&#xff0c;内核为CA55四核最高主频为1.5Ghz处理器。SOC内置集成一个64位的四核RISC处理器&#xff0c;先进的图像信号处理器&#xff08;ISP&#xff09;&#xff0c;高性能的H.265/H.264/MJPEG视频编解码器&a…

5个精美的wordpress中文企业主题模板

元宇宙WordPress主题模板 简洁大气的元宇宙 Metaverse WordPress主题模板&#xff0c;适合元宇宙行业的企业官网使用。 https://www.jianzhanpress.com/?p3292 职业技术培训WordPress主题模板 简洁大气的职业技术培训WordPress主题&#xff0c;适合用于搭建教育培训公司官方…

宏观视角下的浏览器

宏观视角下的浏览器 Chrome架构线程 VS 进程进程架构 TCP协议IP&#xff1a;把数据包送达目的主机UDP&#xff1a;把数据包送达应用程序TCP&#xff1a;把数据完整地送达应用程序 HTTP请求流程构建请求查找缓存准备IP地址和端口等待TCP队列建立TCP连接发送HTTP请求 服务器端处理…

【Flink集群RPC通讯机制(四)】集群组件(tm、jm与rm)之间的RPC通信

文章目录 1. 集群内部通讯方法概述2. TaskManager向ResourceManager注册RPC服务3. JobMaster向ResourceManager申请Slot计算资源 现在我们已经知道Flink中RPC通信框架的底层设计与实现&#xff0c;接下来通过具体的实例了解集群运行时中组件如何基于RPC通信框架构建相互之间的调…

vue3 vite 经纬度逆地址解析

在web端测试经纬度逆地址解析有2中方式&#xff0c;先准备好两个应用key 第一种&#xff0c;使用“浏览器端”应用类型 const address ref() const latitude ref() // 经度 const longitude ref() // 纬度 const ak 你的key // 浏览器端 function getAddressWeb() {// 创建…

C#,动态规划(DP)丢鸡蛋问题(Egg Dropping Puzzle)的三种算法与源代码

1 扔鸡蛋问题 动态规划&#xff08;Dynamic Programming&#xff0c;DP&#xff09;是运筹学的一个分支&#xff0c;是求解决策过程最优化的过程。20世纪50年代初&#xff0c;美国数学家贝尔曼&#xff08;R.Bellman&#xff09;等人在研究多阶段决策过程的优化问题时&#xf…