内核对象kobject和sysfs(4)——kset分析


从狭义上来说,kset就是kobj的一个再封装而已。在封装之后,提供了针对kset之下所有kobj统一管理的一些方法。

我们还是从结构说起:

168 struct kset {
169 struct list_head list;
170 spinlock_t list_lock;
171 struct kobject kobj;
172 const struct kset_uevent_ops *uevent_ops;
173 };

由结构体看见,kset不过是内嵌了一个kobj,在此基础上额外提供了别的操作。在后面统一设备模型中,会讲到类设备就是采用这种方式进行操作的。

由于kset内嵌kobj,那么其操作方法必然和kobj类似。kset的初始化和注册函数合并成了一个函数:

 810 int kset_register(struct kset *k)
811 {
812 int err;
813
814 if (!k)
815 return -EINVAL;
816
817 kset_init(k);
818 err = kobject_add_internal(&k->kobj);
819 if (err)
820 return err;
821 kobject_uevent(&k->kobj, KOBJ_ADD);
822 return 0;
823 }
824 EXPORT_SYMBOL(kset_register); 767 void kset_init(struct kset *k)
768 {
769 kobject_init_internal(&k->kobj);
770 INIT_LIST_HEAD(&k->list);
771 spin_lock_init(&k->list_lock);
772 }

可以看出来,在821行之前,kset的行为和kobj一模一样。kobj相关内容在上一篇有过详细分析,这里重点就变成了分析kset的额外部分。kobject_uevent最终调用kobject_uevent_env,这个函数旨在通过环境变量发送一个uevent事件。下面展开这个函数:

164 int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
165 char *envp_ext[])
166 {
167 struct kobj_uevent_env *env;
168 const char *action_string = kobject_actions[action];
169 const char *devpath = NULL;
170 const char *subsystem;
171 struct kobject *top_kobj;
172 struct kset *kset;
173 const struct kset_uevent_ops *uevent_ops;
174 int i = 0;
175 int retval = 0;
176 #ifdef CONFIG_NET
177 struct uevent_sock *ue_sk;
178 #endif
179
180 pr_debug("kobject: '%s' (%p): %s\n",
181 kobject_name(kobj), kobj, __func__);
182
183 /* search the kset we belong to */
184 top_kobj = kobj;
185 while (!top_kobj->kset && top_kobj->parent)
186 top_kobj = top_kobj->parent;
187
188 if (!top_kobj->kset) {
189 pr_debug("kobject: '%s' (%p): %s: attempted to send uevent "
190 "without kset!\n", kobject_name(kobj), kobj,
191 __func__);
192 return -EINVAL;
193 }
194
195 kset = top_kobj->kset;
196 uevent_ops = kset->uevent_ops;
197
198 /* skip the event, if uevent_suppress is set*/
199 if (kobj->uevent_suppress) {
200 pr_debug("kobject: '%s' (%p): %s: uevent_suppress "
201 "caused the event to drop!\n",
202 kobject_name(kobj), kobj, __func__);
203 return 0;
204 }
205 /* skip the event, if the filter returns zero. */
206 if (uevent_ops && uevent_ops->filter)
207 if (!uevent_ops->filter(kset, kobj)) {
208 pr_debug("kobject: '%s' (%p): %s: filter function "
209 "caused the event to drop!\n",
210 kobject_name(kobj), kobj, __func__);
211 return 0;
212 }
213
214 /* originating subsystem */
215 if (uevent_ops && uevent_ops->name)
216 subsystem = uevent_ops->name(kset, kobj);
217 else
218 subsystem = kobject_name(&kset->kobj);
219 if (!subsystem) {
220 pr_debug("kobject: '%s' (%p): %s: unset subsystem caused the "
221 "event to drop!\n", kobject_name(kobj), kobj,
222 __func__);
223 return 0;
224 }
225
226 /* environment buffer */
227 env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
228 if (!env)
229 return -ENOMEM;
230
231 /* complete object path */
232 devpath = kobject_get_path(kobj, GFP_KERNEL);
233 if (!devpath) {
234 retval = -ENOENT;
235 goto exit;
236 }
237
238 /* default keys */
239 retval = add_uevent_var(env, "ACTION=%s", action_string);
240 if (retval)
241 goto exit;
242 retval = add_uevent_var(env, "DEVPATH=%s", devpath);
243 if (retval)
244 goto exit;
245 retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
246 if (retval)
247 goto exit;
248
249 /* keys passed in from the caller */
250 if (envp_ext) {
251 for (i = 0; envp_ext[i]; i++) {
252 retval = add_uevent_var(env, "%s", envp_ext[i]);
253 if (retval)
254 goto exit;
255 }
256 }
257
258 /* let the kset specific function add its stuff */
259 if (uevent_ops && uevent_ops->uevent) {
260 retval = uevent_ops->uevent(kset, kobj, env);
261 if (retval) {
262 pr_debug("kobject: '%s' (%p): %s: uevent() returned "
263 "%d\n", kobject_name(kobj), kobj,
264 __func__, retval);
265 goto exit;
266 }
267 }
268
269 /*
270 * Mark "add" and "remove" events in the object to ensure proper
271 * events to userspace during automatic cleanup. If the object did
272 * send an "add" event, "remove" will automatically generated by
273 * the core, if not already done by the caller.
274 */
275 if (action == KOBJ_ADD)
276 kobj->state_add_uevent_sent = 1;
277 else if (action == KOBJ_REMOVE)
278 kobj->state_remove_uevent_sent = 1;
279
280 mutex_lock(&uevent_sock_mutex);
281 /* we will send an event, so request a new sequence number */
282 retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)++uevent_seqnum);
283 if (retval) {
284 mutex_unlock(&uevent_sock_mutex);
285 goto exit;
286 } 355
356 exit:
357 kfree(devpath);
358 kfree(env);
359 return retval;
360 }

函数中删除了部分由宏定义控制的非关键代码。

185行的循环表示从当前的对象,一直向根查找,直到查找到某个内核对象包含有kset为止。我们先忽略kset内部嵌入的kobj,把kset和kobj看成是两个独立的类型。那么情况就变得简单了。即该代码假定,需要发送uevent事件,必须依赖于kset,如果本对象没有kset,那么就向上查找kset为止,这样的循环把kset下的所有内核模块,包括该内核模块下的内核模块也都包括在一个集合内。这种模型,也遵循就近选择kset的原则。

如上图所示的关系:

如果寻找kobj5的kset,将会找到kset2,如果寻找kobj3的kset,将会找到kset1.

简单的把kobj和kset看成两个独立的概念,问题迎刃而解。但是实际情况却是kset内嵌一个kobj,这又怎么理解呢?这就回到文章开始提到的,把kset当做是一种特殊的kobj。kset有自己的功能去实现,但是需要借用kobj与sysfs交互的功能以及kobj的层次关系建立起自己的下属成员。

在上述的循环里,还有一个问题。那就是,我们的这次发送uevent事件是在注册的时候发送的,那kset内部的kobj向上找,又能找到什么kset呢?其实,这个就开发者自己去赋值了。在调用kset_register之前,一定要对内嵌的内嵌的kobj的kset指向相应的kset。这样,关系就变成了下图:

在内核中,kset1一般指的是顶层的kset,没有kset类的父类,即不受管制,例如class、pci下的devices。因此,开发人员开发也是从kset2开始开发。当然,如果你希望开发出1个直接在sysfs目录下的kset,当然也可以。但是这时候,while循环将找不到对应的kset,将导致无法发送事件。发送事件错误在注册kset的过程中,不是致命的。毕竟,第一个kset无法向上发送事件,这很好理解。

而如果某个kobj的kset域不为空,将在kobject_add的时候,将自身的连接件链入kset中的head域。kset也是通过该头找到它所管理的所有的kobj,包括自身内嵌的那个kobj。

如果创建的是kset2。那么196行我们看到,kset2将继承kset1的操作方法。该数据结构展开如下:

132 struct kset_uevent_ops {
133 int (* const filter)(struct kset *kset, struct kobject *kobj);
134 const char *(* const name)(struct kset *kset, struct kobject *kobj);
135 int (* const uevent)(struct kset *kset, struct kobject *kobj,
136 struct kobj_uevent_env *env);
137 };
138

由此也可以推断,一个kset内实现的方法,是由其下级的内核模块或kset使用的。在之后,就由具体的上级实现的操作集合里的方法,辅助加以处理逻辑,这里不再赘述。

总结一下:kobj通过parent的逻辑关系,形成了sysfs内的层级分明的目录。kset利用内嵌的kobj也形成了自己在sysfs内的层级分明的目录。但是这并不是kset本身的父子关系。kset的作用是对集合内的其他kobj有统一管理的能力。于是,kset就通过内嵌的kobj内的kset指针指向来确定自己的父亲。如果为NULL,表示自己不受管理。

内核对象kobject和sysfs(4)——kset分析的更多相关文章

  1. 内核对象kobject和sysfs(3)——kobj分析

    内核对象kobject和sysfs(3)--kobj分析 在分析kobj之前,先总结下kobj的功能: 实现结构的动态管理: 实现内核对象到sysfs的映射: 实现自定义属性的管理. 关注一下kobj ...

  2. 内核对象kobject和sysfs(2)——kref分析

    内核对象kobject和sysfs(2)--kref分析 在介绍ref之前,先贴上kref的结构: struct kref { atomic_t refcount; }; 可以看到,kref只是包含一 ...

  3. 内核对象kobject和sysfs(1)——概述

    内核对象kobject和sysfs(1)--概述 问题: 在走读驱动代码时,经常看见kref,kobj,sysfs这些结构,这些结构到底有什么作用?如何理解并使用这些结构呢?这将在接下来的这一系列文章 ...

  4. sysfs - 用于导出内核对象(kobject)的文件系统

    sysfs - _The_ filesystem for exporting kernel objects.sysfs - 用于导出内核对象(kobject)的文件系统Patrick Mochel & ...

  5. [4]Windows内核情景分析---内核对象

    写过Windows应用程序的朋友都常常听说"内核对象"."句柄"等术语却无从得知他们的内核实现到底是怎样的, 本篇文章就揭开这些技术的神秘面纱. 常见的内核对象 ...

  6. linux设备驱动模型之Kobject、kobj_type、kset【转】

    本文转载自:http://blog.csdn.net/fengyuwuzu0519/article/details/74838165 版权声明:本文为博主原创文章,转载请注明http://blog.c ...

  7. Linux 内核文档翻译 - kobject.txt

    原文地址:Linux 内核文档翻译 - kobject.txt 作者:qh997 Everything you never wanted to know about kobjects, ksets, ...

  8. Linux内核文档翻译——kobject.txt

    ==================================================================== Everything you never wanted to ...

  9. 第9章 用内核对象进行线程同步(4)_死锁(DeadLock)及其他

    9.7 线程同步对象速查表 对象 何时处于未触发状态 何时处于触发状态 成功等待的副作用 进程 进程仍在运行的时候 进程终止的时(ExitProcess.TerminateProcess) 没有 线程 ...

随机推荐

  1. Realm的简单使用

    Realm个人理解: 它主要是一套移动端数据库框架,将对数据库表的操作转换为对对象的操作,如果是学过Java ORM框架的同学使用Realm起来应该上手比较快一点.而且据我所知Realm不仅支持Obj ...

  2. C# 计时器写法

        刚才一个交流群里有人问计时器怎么写,正好我也不太熟,就写了个demo,和大家分享一下这个是参考师傅的写的! 计时器有好多种写法,这里给大家推荐一个性能比较好的,用dispatchertimer ...

  3. HTML基础了解

     对HTML最基本的认识和编写:"我的第一个网页" HTML是什么: 它的全称是Hyper Text Markup Language超文本标记语言,页面中包括有视频.图片.链接等其 ...

  4. postgresql初体验

    docker pull orchardup/postgresql docker run -d -p 5432:5432 -e POSTGRESQL_USER=test -e POSTGRESQL_PA ...

  5. Rest模式get,put,post,delete含义与区别(转)

    POST   /uri     创建 DELETE /uri/xxx 删除 PUT    /uri/xxx 更新或创建 GET    /uri/xxx 查看 GET操作是安全的.所谓安全是指不管进行多 ...

  6. JAVAEE学习路线分享

    今天把我的教学经验分享给大家.适合大多数人的学习路线.注:目前作者已经转行做java培训. 首先是培养兴趣.先开始学习HTML知识.也就是做网页,从这里开始比较简单,就是几个标签单词需要记住. 接着开 ...

  7. Swift区间运算符

    Swift 提供了两个方便表达一个区间的值的运算符. 闭区间运算符 闭区间运算符(a...b)定义一个包含从a到b(包括a和b)的所有值的区间. ‌ 闭区间运算符在迭代一个区间的所有值时是非常有用的, ...

  8. Vue.js 介绍入门

    Vue.js 的目标 是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件.Vue.js 是一个用于创建 Web 交互界面的库.它让你通过简单而灵活的 API 创建由数据驱动的 UI 组件. ...

  9. solr学习-基础环境搭建(一)

    目前网上关于solr6.+的安装教程很少,有些6.0之前的教程在应用到6.+的版本中出现很多的问题,所以特别整理出来这一片文章,希望能给各位码农一些帮助! 很少写些文章,如有不对的地方,还希望多多指导 ...

  10. Flex Robotlegs

    Flex Robotlegs 一.基于Robotlegs框架 flex应用基本组成 ProjectNameContext.as 用于配置 Robotlegs 的映射 ProjectName.mxml ...