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

news/2024/12/22 20:45:44/

一,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.ppmy.cn/news/1365070.html

相关文章

来分析两道小题

一、源码 二、分析 首先它会接两个参数一个是id一个是ps&#xff0c;传递的话会包含一个flag.php&#xff0c;然后数据库连接&#xff0c;之后传递过滤&#xff0c;然后查询&#xff0c;如果查到了就会取id&#xff0c;取出来看是不是跟adog一样&#xff0c;如果是它告诉你账号…

备战蓝桥杯————k个一组反转单链表

k个反转单链表&#xff0c;顾名思义就是k个节点为一组进行反转&#xff0c;这是一道困难的题目&#xff0c;如何解答&#xff0c;可以在我们前面的反转链表中得到思路。 如何 K 个一组反转单链表 题目描述 给你链表的头节点 head &#xff0c;每 k 个节点一组进行翻转&#xf…

React引入css的几种方式以及应用

1.直接引入css文件 import "./parent.css" 2.引入css模块&#xff0c;定义文件名[组件名.module.css]&#xff1b;该方式可避免类名的重复&#xff0c;每个组件都有独立的作用域&#xff0c;避免了全局污染&#xff0c;保证了类名的唯一性 import styles from &qu…

300分钟吃透分布式缓存-15讲:如何深入理解、应用及扩展 Twemproxy?

Twemproxy 架构及应用 Twemproxy 是 Twitter 的一个开源架构&#xff0c;它是一个分片资源访问的代理组件。如下图所示&#xff0c;它可以封装资源池的分布及 hash 规则&#xff0c;解决后端部分节点异常后的探测和重连问题&#xff0c;让 client 访问尽可能简单&#xff0c;同…

vue3编写H5适配横竖屏

具体思路如下&#xff1a; 1、监听浏览器屏幕变化&#xff0c;通过监听屏幕宽高&#xff0c;辨别出是横屏&#xff0c;还是竖屏状态 在项目的起始根页面进行监听&#xff0c;我就是在App.vue文件下进行监听 代码如下&#xff1a; <template><RouterView /> <…

Mycat核心教程--Mycat 监控工具【四】

Mycat核心教程--Mycat 监控工具 九、Mycat 监控工具9.1.Mycat-web 简介9.2.Mycat-web 配置使用9.2.1.ZooKeeper 安装【上面有】9.2.2.Mycat-web 安装9.2.2.1.下载安装包9.2.2.2.安装包拷贝到Linux系统/opt目录下&#xff0c;并解压9.2.2.3.拷贝mycat-web文件夹到/usr/local目录…

LVS负载均衡服务器

简介: LVS (Linux Virtual Server):四层路由设备&#xff0c;是由中国人章文松研发的(阿里巴巴的副总裁)根据用户请求的IP与端口号实现将用户的请求分发至不同的主机。 工作原理: LVS工作在一台server上提供Directory(负载均衡器)的功能&#xff0c;本身并不提供服务&#xff…

s-table和columns初始化不完整,造成table文件的filter报错

问题 顺藤摸瓜找errorHandler.js文件 发现文件并没有什么问题 顺藤摸瓜找index.vue文件 首先找到报错的filter&#xff0c;发现与columnsSetting相关 找到columnsSetting发现等于columns 返回自己使用S-table组件的地方&#xff0c;发现columns初始化时仅初始化为ref()未表明…