Linux 设备模型之 (kobject、kset 和 Subsystem)(二)

问题描写叙述:前文我们知道了/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_unregister 来释放之前创建的 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操作函数的封装,以实现子系统的操作*/

时间: 2024-12-23 20:59:21

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

linux设备模型之kobject

kobject 结构 在Linux内核里,kobject是组成Linux设备模型的基础,一个kobject对应sysfs里的 一个目录.从面向对象的角度来说,kobject可以看作是所有设备对象的基类,因为C 语言并没有面向对象的语法,所以一般是把kobject内嵌到其他结构体里来实现类似的 作用,这里的其他结构体可以看作是kobject的派生类.Kobject为Linux设备模型提 供了很多有用的功能,比如引用计数,接口抽象,父子关系等等.引用计数本质上就 是利用kref实现的. 另外,Lin

Linux 设备模型之 kobject 内嵌结构

问题描述:前文我们知道了/sys是包含内核和驱动的实施信息的,用户可以通过 /sys 这个接口,用户通过这个接口可以一览内核设备的全貌.本文将从Linux内核的角度来看一看这个设备模型是如何构建的. 1.kobject 结构 在Linux内核里,kobject是组成Linux设备模型的基础,一个kobject对应sysfs里的一个目录.从面向对象的角度来说,kobject可以看作是所有设备对象的基类,因为C语言并没有面向对象的语法,所以一般是把kobject内嵌到其他结构体里来实现类似的作用,这

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

Linux2.6以后的设备驱动,都是在设备模型的基础上构建的,因此,要编写linux下的设备驱动程序,不论是usb设备,pci设备等,都需要了解设备模型. 设备模型的基础结构体主要是kobject,kset这两个结构体: struct kobject {    char      * k_name;    char      name[KOBJ_NAME_LEN];    struct kref    kref;    struct list_head  entry;    struct kob

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

本文将对Linux系统中的sysfs进行简单的分析,要分析sysfs就必须分析内核的driver-model(驱动模型),两者是紧密联系的.在分析过程中,本文将以platform总线和spi主控制器的platform驱动为例来进行讲解.其实,platform机制是基于driver-model的,通过本文,也会对platform机制有个简单的了解. 内核版本:2.6.30 1. What is sysfs? 个人理解:sysfs向用户空间展示了驱动设备的层次结构.我们都知道设备和对应的驱动都是由内

转 linux设备模型(4)

作者:wwang 出处:http://www.cnblogs.com/wwang 本文采用知识共享署名-非商业性使用-相同方式共享 2.5 中国大陆许可协议进行许可,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接. <Linux设备模型 (2)>和<Linux设备模型 (3)>主要通过一些简单的实作介绍了kobject.kset.kobj_type.attribute等数据结构的用法,但这些实作并没有涉及到实际环境下的设备模型和sysfs.本文将以/sy

转 Linux设备模型 (1)

作者:wwang 出处:http://www.cnblogs.com/wwang 本文采用知识共享署名-非商业性使用-相同方式共享 2.5 中国大陆许可协议进行许可,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接. 随着计算机的周边外设越来越丰富,设备管理已经成为现代操作系统的一项重要任务,这对于Linux来说也是同样的情况.每次Linux内核新版本的发布,都会伴随着一批设备驱动进入内核.在Linux内核里,驱动程序的代码量占有了相当大的比重.下图是我在网络上搜索到的

【linux设备模型】之platform设备驱动

一.platform总线.设备和驱动 platform是一种虚拟总线,对应的设备称为platform_device,对应的驱动称为platform_driver. platform_device定义在<linux/platform_device.h>中: 1 struct platform_device { 2 const char * name; 3 int id; 4 struct device dev; 5 u32 num_resources; 6 struct resource * r

linux设备模型之led子系统(转载)

linux设备模型之led子系统 本文来自转载: http://www.cnblogs.com/gdt-a20 时代不同了,连led都成子系统了,针对内核提供的通用模型,分析一下,好久没写文章了也! 代码位于drivers/leds下,看一下Makefile 模型文件主要是: # LED Core obj-$(CONFIG_NEW_LEDS)            += led-core.o obj-$(CONFIG_LEDS_CLASS)        += led-class.o obj-$

linux设备模型

为了降低设备多样性带来的Linux驱动开发的复杂度,以及设备热拔插处理.电源管理等,Linux内核提出了设备模型(也称作Driver Model)的概念.设备模型将硬件设备归纳.分类,然后抽象出一套标准的数据结构和接口.驱动的开发,就简化为对内核所规定的数据结构的填充和实现. 我们知道linux内核中常见的的总线有I2C总线,PCI总线,串口总线,SPI总线,PCI总线,CAN总线,单总线等,所以有些设备和驱动就可以挂在这些总线上,然后通过总线上的match进行设备和驱动的匹配.但是有的设备并不