问题描写叙述:前文我们知道了/sys是包括内核和驱动的实施信息的,用户能够通过 /sys 这个接口。用户通过这个接口能够一览内核设备的全貌。本文将从Linux内核的角度来看一看这个设备模型是怎样构建的。

1、kobject 结构

在Linux内核里,kobject是组成Linux设备模型的基础,一个kobject相应sysfs里的一个文件夹。

从面向对象的角度来说。kobject能够看作是全部设备对象的基类,由于C语言并没有面向对象的语法,所以通常是把kobject内嵌到其它结构体里来实现类似的作用,这里的其它结构体能够看作是kobject的派生类。Kobject为Linux设备模型提供了非常多实用的功能,比方引用计数。接口抽象,父子关系等等。

引用计数本质上就是利用kref实现的。

另外。Linux设备模型另一个重要的数据结构kset。Kset本身也是一个kobject,所以它在sysfs里相同表现为一个文件夹,但它和kobject的不同之处在于kset能够看作是一个容器,假设你把它类比为C++里的容器类如list也无不可。Kset之所以能作为容器来使用,其内部正是内嵌了一个双向链表结构struct
list_head。

kobject 在内核中的描写叙述

struct kobject {
const char *name;
struct list_head entry;
struct kobject *parent;
struct kset *kset;
struct kobj_type *ktype;
struct sysfs_dirent *sd;
struct kref kref;
unsigned int state_initialized:1;
unsigned int state_in_sysfs:1;
unsigned int state_add_uevent_sent:1;
unsigned int state_remove_uevent_sent:1;
unsigned int uevent_suppress:1;
};

内核里的设备之间是以树状形式组织的,在这样的组织架构里比較靠上层的节点能够看作是下层节点的父节点,反映到sysfs里就是上级文件夹和下级文件夹之间的关系。在内核里,正是kobject帮助我们实现这样的父子关系。在kobject的定义里,name表示的是kobject在sysfs中的名字;指针parent用来指向kobject的父对象;Kref大家应该比較熟悉了。kobject通过它来实现引用计数;Kset指针用来指向这个kobject所属的kset,下文会再具体描写叙述kset的使用方法。对于ktype,假设仅仅是望文生义的话。应该是用来描写叙述kobject的类型信息。

Ktype的定义例如以下:

struct kobj_type {
void (*release)(struct kobject *kobj);
const struct sysfs_ops *sysfs_ops;
struct attribute **default_attrs;
};

函数指针release是给kref使用的。当引用计数为0这个指针指向的函数会被调用来释放内存。sysfs_ops和attribute是做什么用的呢?前文里提到。一个kobject相应sysfs里的一个文件夹。而文件夹下的文件就是由sysfs_ops和attribute来实现的,当中。attribute定义了kobject的属性,在sysfs里相应一个文件,sysfs_ops用来定义读写这个文件的方法。Ktype里的attribute是默认的属性,另外也能够使用更加灵活的手段,本文的重点还是放在default
attribute。

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/kobject.h> struct my_kobj { //内嵌kobject的结构
int val;
struct kobject kobj;
}; struct my_kobj *obj1, *obj2;
struct kobj_type my_type; struct attribute name_attr = {
.name = "name", //文件名称
.mode = 0444, //指定文件的訪问权限
}; struct attribute val_attr = {
.name = "val", //文件名称
.mode = 0666, //指定文件的訪问权限
}; struct attribute *my_attrs[] = {
&name_attr,
&val_attr,
NULL,
}; /*
结构体struct attribute里的name变量用来指定文件名称。mode变量用来指定文件的訪问权限。
这里须要着重指出的是,数组my_attrs的最后一项一定要赋为NULL,否则会造成内核oops。 */
ssize_t my_show(struct kobject *kobj, struct attribute *attr, char *buffer)
{
struct my_kobj *obj = container_of(kobj, struct my_kobj, kobj);
ssize_t count = 0; if (strcmp(attr->name, "name") == 0) {
count = sprintf(buffer, "%s\n", kobject_name(kobj));
} else if (strcmp(attr->name, "val") == 0) {
count = sprintf(buffer, "%d\n", obj->val);
} return count;
} ssize_t my_store(struct kobject *kobj, struct attribute *attr, const char *buffer, size_t size)
{
struct my_kobj *obj = container_of(kobj, struct my_kobj, kobj); if (strcmp(attr->name, "val") == 0) {
sscanf(buffer, "%d", &obj->val);
} return size;
} struct sysfs_ops my_sysfsops = {
.show = my_show,
.store = my_store,
}; void obj_release(struct kobject *kobj)
{
struct my_kobj *obj = container_of(kobj, struct my_kobj, kobj);
printk(KERN_INFO "obj_release %s\n", kobject_name(&obj->kobj));
kfree(obj);
} static int __init mykobj_init(void)
{
printk(KERN_INFO "mykobj_init\n"); obj1 = kzalloc(sizeof(struct my_kobj), GFP_KERNEL); //分配obj1和obj2并赋值
if (!obj1) {
return -ENOMEM;
}
obj1->val = 1; obj2 = kzalloc(sizeof(struct my_kobj), GFP_KERNEL);
if (!obj2) {
kfree(obj1);
return -ENOMEM;
}
obj2->val = 2; my_type.release = obj_release;
my_type.default_attrs = my_attrs;
my_type.sysfs_ops = &my_sysfsops; kobject_init_and_add(&obj1->kobj, &my_type, NULL, "mykobj1"); /*函数来初始化kobject并把它增加到设备模型的体系架构*/
kobject_init_and_add(&obj2->kobj, &my_type, &obj1->kobj, "mykobj2");
/*
kobject_init用来初始化kobject结构,kobject_add用来把kobj增加到设备模型之中。
在实作中,我们先对obj1进行初始化和增加的动作,调用參数里,parent被赋为NULL,表示obj1没有父对象,反映到sysfs里,
my_kobj1的文件夹会出如今/sys下。obj2的父对象设定为obj1,那么my_kobj2的文件夹会出如今/sys/my_kobj1以下。
前面提到。kobject也提供了引用计数的功能,尽管本质上是利用kref,但也提供了另外的接口供用户使用。 kobject_init_and_add和kobject_init这两个函数被调用后。kobj的引用计数会初始化为1,
所以在module_exit时要记得用kobject_put来释放引用计数。
*/
return 0;
} static void __exit mykobj_exit(void)
{
printk(KERN_INFO "mykobj_exit\n"); kobject_del(&obj2->kobj); /*先子对象。后父对象*/
kobject_put(&obj2->kobj); kobject_del(&obj1->kobj);
kobject_put(&obj1->kobj); return;
}
/*
kobject_del的作用是把kobject从设备模型的那棵树里摘掉。同一时候sysfs里对应的文件夹也会删除。
这里须要指出的是。释放的顺序应该是先子对象,后父对象。 由于kobject_init_and_add和kobject_add这两个函数会调用kobject_get来增加父对象的引用计数,
所以kobject_del须要调用kobject_put来降低父对象的引用计数。在本例中,假设先通过kobject_put来释放obj1,
那kobject_del(&obj2->kobj)就会出现内存错误。
*/
module_init(mykobj_init);
module_exit(mykobj_exit); MODULE_LICENSE("GPL");

1.1 kobject 自我总结

kobject是组成Linux设备模型的基础,一个kobject相应sysfs里的一个文件夹。事实上它存在的意义在于把高级对象连接到设备模型上。所以kobject
被嵌入到其它结构中。kobject 可被看作一个最顶层的基类,其它类都它的派生产物。 kobject 实现了一系列方法,对自身并没有特殊作用,而对其它对象却很有效。

。

初始它仅仅被作为一个简单的引用计数, 但随时间的推移,其任务越来越多。如今kobject 所处理的任务和支持代码包含 : 对象的引用计数 :跟踪对象生命周期的一种方法是使用引用计数。

当没有内核代码持有该对象的引用时,
该对象将结束自己的有效生命期并可被删除。

本文代码下载

2、kset

在这个实作中,我们建立了两个对象obj1和obj2。obj1是obj2的父对象。假设推广开来。obj1能够有很多其它的子对象。在Linux内核中,这样的架构方式事实上并无太大的实际价值。有限的用处之中的一个是在sysfs里创建子文件夹(Linux内核里有这样的使用方法,这样的情况下。直接调用内核提供的kobject_create来实现,不须要自己定义数据结构并内嵌kobject),并且。创建子文件夹也是有其它的办法的。我们知道。Linux设备模型最初的目的是为了方便电源管理,这就须要从上到下的遍历,在这样的架构里,通过obj1并无法訪问其全部的子对象。这个实作最大的意义在于能够让我们比較清晰的理解kobject怎样使用。通常情况下,kobject仅仅须要在叶节点里使用,上层的节点要使用kset。

struct kset {
struct list_head list;
spinlock_t list_lock;
struct kobject kobj;
const struct kset_uevent_ops *uevent_ops;
};

Kset结构里的kobj表明它也是一个kobject,list变量用来组织它全部的子对象。

<span style="font-family:Microsoft YaHei;font-size:12px;">#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/kobject.h> struct my_kobj { //内嵌kobject的结构
int val;
struct kobject kobj;
}; struct my_kobj *obj1, *obj2;
struct kset *my_kset;
struct kobj_type my_type; struct attribute name_attr = {
.name = "name", //文件名称
.mode = 0444, //指定文件的訪问权限
}; struct attribute val_attr = {
.name = "val", //文件名称
.mode = 0666, //指定文件的訪问权限
}; struct attribute *my_attrs[] = {
&name_attr,
&val_attr,
NULL,
};
/*
结构体struct attribute里的name变量用来指定文件名称,mode变量用来指定文件的訪问权限。
这里须要着重指出的是。数组my_attrs的最后一项一定要赋为NULL,否则会造成内核oops。 */ ssize_t my_show(struct kobject *kobj, struct attribute *attr, char *buffer)
{
struct my_kobj *obj = container_of(kobj, struct my_kobj, kobj);
ssize_t count = 0; if (strcmp(attr->name, "name") == 0) {
count = sprintf(buffer, "%s\n", kobject_name(kobj));
} else if (strcmp(attr->name, "val") == 0) {
count = sprintf(buffer, "%d\n", obj->val);
} return count;
} ssize_t my_store(struct kobject *kobj, struct attribute *attr, const char *buffer, size_t size)
{
struct my_kobj *obj = container_of(kobj, struct my_kobj, kobj); if (strcmp(attr->name, "val") == 0) {
sscanf(buffer, "%d", &obj->val);
} return size;
} struct sysfs_ops my_sysfsops = {
.show = my_show,
.store = my_store,
}; void obj_release(struct kobject *kobj)
{
struct my_kobj *obj = container_of(kobj, struct my_kobj, kobj);
printk(KERN_INFO "obj_release %s\n", kobject_name(&obj->kobj));
kfree(obj);
} static int __init mykset_init(void)
{
printk(KERN_INFO "mykset_init\n"); my_kset = kset_create_and_add("my_kset", NULL, NULL);
if (!my_kset) {
return -ENOMEM;
} obj1 = kzalloc(sizeof(struct my_kobj), GFP_KERNEL);
if (!obj1) {
kset_unregister(my_kset);
return -ENOMEM;
}
obj1->val = 1; obj2 = kzalloc(sizeof(struct my_kobj), GFP_KERNEL);
if (!obj2) {
kset_unregister(my_kset);
kfree(obj1);
return -ENOMEM;
}
obj2->val = 2; obj1->kobj.kset = my_kset;
obj2->kobj.kset = my_kset; my_type.release = obj_release;
my_type.default_attrs = my_attrs;
my_type.sysfs_ops = &my_sysfsops; kobject_init_and_add(&obj1->kobj, &my_type, NULL, "mykobj1");/*函数来初始化kobject并把它增加到设备模型的体系架构*/
kobject_init_and_add(&obj2->kobj, &my_type, NULL, "mykobj2");
/*
kobject_init用来初始化kobject结构,kobject_add用来把kobj增加到设备模型之中。
在实作中,我们先对obj1进行初始化和增加的动作。调用參数里。parent被赋为NULL。表示obj1没有父对象,反映到sysfs里。
my_kobj1的文件夹会出如今/sys下,obj2的父对象设定为obj1,那么my_kobj2的文件夹会出如今/sys/my_kobj1以下。 前面提到,kobject也提供了引用计数的功能,尽管本质上是利用kref,但也提供了另外的接口供用户使用。
kobject_init_and_add和kobject_init这两个函数被调用后,kobj的引用计数会初始化为1,
所以在module_exit时要记得用kobject_put来释放引用计数。 */
return 0;
} static void __exit mykset_exit(void)
{
printk(KERN_INFO "mykset_exit\n"); kobject_del(&obj1->kobj);/*先子对象,后父对象*/
kobject_put(&obj1->kobj); kobject_del(&obj2->kobj);
kobject_put(&obj2->kobj); kset_unregister(my_kset); return;
}
/*
kobject_del的作用是把kobject从设备模型的那棵树里摘掉,同一时候sysfs里对应的文件夹也会删除。
这里须要指出的是,释放的顺序应该是先子对象。后父对象。 由于kobject_init_and_add和kobject_add这两个函数会调用kobject_get来增加父对象的引用计数,
所以kobject_del须要调用kobject_put来降低父对象的引用计数。在本例中,假设先通过kobject_put来释放obj1。
那kobject_del(&obj2->kobj)就会出现内存错误。
*/ module_init(mykset_init);
module_exit(mykset_exit); MODULE_LICENSE("GPL");
</span>

在 module_init 里,我们首先调用 kset_create_and_add 创建 my_kset,接下来把 my_kset 赋给 obj1 和 obj2 ,最后调用 kobject_init_and_add
加入 obj1 和 obj2。这里须要注意的是,kobject_init_and_add 參数里的 parent 都是NULL,在这样的情况下。obj1 和 obj2 的父对象由 kobject 结构里的 kset 指针决定,在这个实作里就是 my_kset。在 module_exit 里。我们还须要额外调用 kset_unreg&#29;ister 来释放之前创建的 my_kset.

2.1 kset 自我总结

kset
象 kobj_type 结构的扩展; 一个 kset 是嵌入到同样类型结构的 kobject 的集合。但 struct kobj_type 关注的是对象的类型,而struct kset 关心的是对象的聚合和集合。其主要功能是包容。可觉得是kobjects 的顶层容器类。每一个 kset 在内部包括自己的 kobject, 并能够用多种处理kobject 的方法处理kset。 ksets 总是在 sysfs 中出现; 一旦设置了 kset 并把它加入到系统中, 将在 sysfs 中创建一个文件夹。kobjects
不必在 sysfs 中表示, 但kset中的每个 kobject 成员都在sysfs中得到表述。

添加 kobject
到 kset 中去,一般是在kobject 创建时完毕,其过程分为2步:

(1)完毕kobject的初始化。特别注意mane和parent和初始化。

(2)把kobject 的 kset 成员指向目标kset。

(3)将kobject 传递给以下的函数

3、子系统

子系统是对整个内核中一些高级部分的表述。子系统通常(但不一定)出如今
sysfs分层结构中的顶层。内核子系统包含 block_subsys(/sys/block 块设备)、 devices_subsys(/sys/devices 核心设备层)以及内核已知的用于各种总线的特定子系统。

对于新的内核已经不再有subsystem数据结构了。用kset取代了。

每一个 kset 必须属于一个子系统。子系统成员帮助内核在分层结构中定位 kset 。

/*子系统通经常使用下面的宏声明:*/

decl_subsys(name, struct kobj_type *type, struct kset_uevent_ops * uevent_ops);

/*子系统的操作函数:*/

void subsystem_init(struct kset *s);

int subsystem_register(struct kset *s);

void subsystem_unregister(struct kset *s);

struct subsystem *subsys_get(struct kset *s)

void subsys_put(struct kset *s);

/*这些函数基本上是kset操作函数的封装,以实现子系统的操作*/

Linux 设备模型之 (kobject、kset 和 Subsystem)(二)的更多相关文章

  1. Linux设备模型之kobject

    阿辉原创,转载请注明出处 参考文档:LDD3-ch14.内核文档Documentation/kobject.txt,本文中使用到的代码均摘自Linux-3.4.75 ----------------- ...

  2. 设备模型之kobject,kset及其关系

    Linux2.6以后的设备驱动,都是在设备模型的基础上构建的,因此,要编写linux下的设备驱动程序,不论是usb设备,pci设备等,都需要了解设备模型. 设备模型的基础结构体主要是kobject,k ...

  3. Linux设备模型分析之kset(基于3.10.1内核)

    作者:刘昊昱 博客:http://blog.csdn.net/liuhaoyutz 内核版本:3.10.1   一.kset结构定义 kset结构体定义在include/linux/kobject.h ...

  4. 【原创】linux设备模型之kset/kobj/ktype分析

    背 景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本 ...

  5. linux设备模型_转

    建议原博文查看,效果更佳. 转自:http://www.cnblogs.com/wwang/category/269350.html Linux设备模型 (1) 随着计算机的周边外设越来越丰富,设备管 ...

  6. Linux设备模型 学习总结

    看LDD3中设备模型一章,觉得思维有些混乱.这里从整体的角度来理理思路.本文从四个方面来总结一些内容: 1.底层数据结构:kobject,kset.2.linux设备模型层次关系:bus_type,d ...

  7. Linux设备模型——设备驱动模型和sysfs文件系统解读

    本文将对Linux系统中的sysfs进行简单的分析,要分析sysfs就必须分析内核的driver-model(驱动模型),两者是紧密联系的.在分析过程中,本文将以platform总线和spi主控制器的 ...

  8. Linux 设备模型浅析之 uevent 篇(2)

    Linux 设备模型浅析之 uevent 篇 本文属本人原创,欢迎转载,转载请注明出处.由于个人的见识和能力有限,不可能面 面俱到,也可能存在谬误,敬请网友指出,本人的邮箱是 yzq.seen@gma ...

  9. linux设备模型:扩展篇

    Linux设备模型组件:总线  一.定义:总线是不同IC器件之间相互通讯的通道;在计算机中,一个总线就是处理器与一个或多个不同外设之间的通讯通道;为了设备模型的目的,所有的设备都通过总线相互连接,甚至 ...

随机推荐

  1. Centos5, 6, 以及Ubuntu18.04下更改系统时间和时区

    http://www.namhuy.net/2435/how-to-change-date-time-timezone-on-centos-6.html 查看日期(使用 -R 参数会以数字显示时区) ...

  2. Android API之android.provider.ContactsContract.RawContacts

    android.provider.ContactsContract.RawContacts Constants for the raw contacts table, which contains o ...

  3. springmvc json结合

    获取json数据     名字一样就获取了 user @RequestMapping("/addUser") public String addUser(User user,Htt ...

  4. jquery json ajax三级联动

    province_city_area.jsp <%@ page language="java" pageEncoding="UTF-8"%> < ...

  5. 【转载】CodeLite汉化

    这几天在Ubuntu下做程序想找一个代码提示功能比较好的IDE但又不想用NETBEANS和ECLIPSE,找到CodeLite但是它是全英文的,比较晕.找了一下,下载了一个windows版的准备回去研 ...

  6. Mac新建文件夹、txt文件、其他格式文件

    Mac新建txt,正好有人问我,我就把我自己的方法记录一下: 先cd到你指定的文件路径下: 新建文件夹: mkdir test 新建txt touch test.txt 新建无后缀格式文件 touch ...

  7. windows平台下压缩tar.gz

    windows平台下很多压缩软件(如360压缩)都支持tar.gz的解压,但不支持压缩 推荐一款支持tar.gz压缩的软件7zip 下面介绍可视化和命令行两种操作方式将文件压缩为tar.gz 可视化操 ...

  8. 【LeetCode】12. Integer to Roman (2 solutions)

    Integer to Roman Given an integer, convert it to a roman numeral. Input is guaranteed to be within t ...

  9. java JAXB + STAX(是一种针对XML的流式拉分析API)读取xml

    JDK1.5需要添加jar包,1.6以后就不需要了<dependency> <groupId>stax</groupId> <artifactId>st ...

  10. RabbitMQ消息队列的小伙伴: ProtoBuf(Google Protocol Buffer) [转]

    什么是ProtoBuf? 一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化.它很适合做数据存储或 RPC 数据交换格式.可用于通讯协议.数据存储等领域的语言无关.平台无关.可扩 ...