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

2024-02-23 23:44

本文主要是介绍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

这篇关于Linux设备模型(二) - kset/kobj/ktype APIs的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/740311

相关文章

SpringBoot快速接入OpenAI大模型的方法(JDK8)

《SpringBoot快速接入OpenAI大模型的方法(JDK8)》本文介绍了如何使用AI4J快速接入OpenAI大模型,并展示了如何实现流式与非流式的输出,以及对函数调用的使用,AI4J支持JDK8... 目录使用AI4J快速接入OpenAI大模型介绍AI4J-github快速使用创建SpringBoot

linux打包解压命令方式

《linux打包解压命令方式》文章介绍了Linux系统中常用的打包和解压命令,包括tar和zip,使用tar命令可以创建和解压tar格式的归档文件,使用zip命令可以创建和解压zip格式的压缩文件,每... 目录Lijavascriptnux 打包和解压命令打包命令解压命令总结linux 打包和解压命令打

linux如何复制文件夹并重命名

《linux如何复制文件夹并重命名》在Linux系统中,复制文件夹并重命名可以通过使用“cp”和“mv”命令来实现,使用“cp-r”命令可以递归复制整个文件夹及其子文件夹和文件,而使用“mv”命令可以... 目录linux复制文件夹并重命名我们需要使用“cp”命令来复制文件夹我们还可以结合使用“mv”命令总

Linux使用cut进行文本提取的操作方法

《Linux使用cut进行文本提取的操作方法》Linux中的cut命令是一个命令行实用程序,用于从文件或标准输入中提取文本行的部分,本文给大家介绍了Linux使用cut进行文本提取的操作方法,文中有详... 目录简介基础语法常用选项范围选择示例用法-f:字段选择-d:分隔符-c:字符选择-b:字节选择--c

Linux使用nload监控网络流量的方法

《Linux使用nload监控网络流量的方法》Linux中的nload命令是一个用于实时监控网络流量的工具,它提供了传入和传出流量的可视化表示,帮助用户一目了然地了解网络活动,本文给大家介绍了Linu... 目录简介安装示例用法基础用法指定网络接口限制显示特定流量类型指定刷新率设置流量速率的显示单位监控多个

ElasticSearch+Kibana通过Docker部署到Linux服务器中操作方法

《ElasticSearch+Kibana通过Docker部署到Linux服务器中操作方法》本文介绍了Elasticsearch的基本概念,包括文档和字段、索引和映射,还详细描述了如何通过Docker... 目录1、ElasticSearch概念2、ElasticSearch、Kibana和IK分词器部署

Linux流媒体服务器部署流程

《Linux流媒体服务器部署流程》文章详细介绍了流媒体服务器的部署步骤,包括更新系统、安装依赖组件、编译安装Nginx和RTMP模块、配置Nginx和FFmpeg,以及测试流媒体服务器的搭建... 目录流媒体服务器部署部署安装1.更新系统2.安装依赖组件3.解压4.编译安装(添加RTMP和openssl模块

linux下多个硬盘划分到同一挂载点问题

《linux下多个硬盘划分到同一挂载点问题》在Linux系统中,将多个硬盘划分到同一挂载点需要通过逻辑卷管理(LVM)来实现,首先,需要将物理存储设备(如硬盘分区)创建为物理卷,然后,将这些物理卷组成... 目录linux下多个硬盘划分到同一挂载点需要明确的几个概念硬盘插上默认的是非lvm总结Linux下多

如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别详解

《如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别详解》:本文主要介绍如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别的相关资料,描述了如何使用海康威视设备网络SD... 目录前言开发流程问题和解决方案dll库加载不到的问题老旧版本sdk不兼容的问题关键实现流程总结前言作为

0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型的操作流程

《0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeekR1模型的操作流程》DeepSeekR1模型凭借其强大的自然语言处理能力,在未来具有广阔的应用前景,有望在多个领域发... 目录0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型,3步搞定一个应