转载自:http://www.360doc.com/content/13/0626/15/12892305_295670482.shtml
在linux的驱动表示中,主要有三个基本的结构,分别是kobject,kset,ktype.Kobject,kset,kypte这三个结构是设备模型中的下层架构。模型中的每一个元素都对应一个kobject.kset和ktype可以看成是kobject在层次结构与属性结构方面的扩充。将三者之间的关系用图的方示描述如下:
如上图所示:我们知道。在sysfs中每一个目录都对应一个kobject.这些kobject都有自己的parent。在没有指定parent的情况下,都会指向它所属的kset->object。其次,kset也内嵌了kobject.这个kobject又可以指它上一级的parent。就这样。构成了一个空间上面的层次关系。
其实,每个对象都有属性。例如,电源管理,执插拨事性管理等等。因为大部份的同类设备都有相同的属性,因此将这个属性隔离开来,存放在ktype中。这样就可以灵活的管理了.记得在分析sysfs的时候。对于sysfs中的普通文件读写操作都是由kobject->ktype->sysfs_ops来完成的.
kobject
truct kobject { const char *name; //名字 struct list_head entry; //连接到kset建立层次结构 struct kobject *parent; //指向父节点,面向对象的层次架构 struct kset *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; };
在Linux内核里,kobject是组成Linux设备模型的基础,一个kobject对应sysfs里的一个目录。从面向对象的角度来说,kobject可以看作是所有设备对象的基类,因为C语言并没有面向对象的语法,所以一般是把kobject内嵌到其他结构体里来实现类似的作用,这里的其他结构体可以看作是kobject的派生类。Kobject为Linux设备模型提供了很多有用的功能,比如引用计数,接口抽象,父子关系等等,所以,在内核中,没有用kobject直接定义的变量,kobject只是作为一个抽象的基类而存在。一般都是将kobject嵌入到另一个结构,这个结构就可以看做是kobject的一个子类。而kobject的子类会比较关心kobject的属性和方法。
内核里的设备之间是以树状形式组织的,在这种组织架构里比较靠上层的节点可以看作是下层节点的父节点,反映到sysfs里就是上级目录和下级目录之间的关系,在内核里,正是kobject帮助我们实现这种父子关系。在kobject的定义里,name表示的是kobject在sysfs中的名字;指针parent用来指向kobject的父对象;Kref大家应该比较熟悉了,kobject通过它来实现引用计数;Kset指针用来指向这个kobject所属的kset;对于ktype,如果只是望文生义的话,应该是用来描述kobject的类型信息。
struct kref//引用计数 { atomic_t refcount; }kobject的作用:
1、sysfs 表述:在 sysfs 中出现的每个对象都对应一个 kobject, 它和内核交互来创建它的可见表述。
2、数据结构关联:整体来看, 设备模型是一个极端复杂的数据结构,通过其间的大量链接而构成一个多层次的体系结构。kobject 实现了该结构并将其聚合在一起。
3、热插拔事件处理 :kobject 子系统将产生的热插拔事件通知用户空间。
一个kobject对自身并不感兴趣,它存在的意义在于把高级对象连接到设备模型上。因此内核代码很少(甚至不知道)创建一个单独的 kobject;而kobject 被用来控制对大型域(domain)相关对象的访问,所以kobject 被嵌入到其他结构中。kobject 可被看作一个最顶层的基类,其他类都它的派生产物。 kobject 实现了一系列方法,对自身并没有特殊作用,而对其他对象却非常有效。
对于给定的kobject指针,可使用container_of宏得到包含它的结构体的指针
kobject相关操作函数:
1、初始化---kobject_init
void kobject_init(struct kobject *kobj, struct kobj_type *ktype) { char *err_str; if (!kobj) { err_str = "invalid kobject pointer!"; goto error; } if (!ktype) { err_str = "must have a ktype to be initialized properly!\n"; goto error; } if (kobj->state_initialized) { /* do not error out as sometimes we can recover */ printk(KERN_ERR "kobject (%p): tried to init an initialized " "object, something is seriously wrong.\n", kobj); dump_stack(); } kobject_init_internal(kobj);//进行主要的成员变量初始化 kobj->ktype = ktype; //<span style="color: rgb(0, 130, 0); font-family: Consolas, 'Courier New', Courier, mono, serif; line-height: 18px; text-align: left; background-color: rgb(248, 248, 248); ">关联</span>这个kobject类型 return; error: printk(KERN_ERR "kobject (%p): %s\n", kobj, err_str); dump_stack(); }
static void kobject_init_internal(struct kobject *kobj) { if (!kobj) return; kref_init(&kobj->kref);//初始化引用计数为1 INIT_LIST_HEAD(&kobj->entry);//prev和next都指向自己 kobj->state_in_sysfs = 0; kobj->state_add_uevent_sent = 0; kobj->state_remove_uevent_sent = 0; kobj->state_initialized = 1; }2、将kobject加入到分层结构-----kobject_add
int kobject_add(struct kobject *kobj, struct kobject *parent, const char *fmt, ...) va_list args; int retval; if (!kobj) return -EINVAL; if (!kobj->state_initialized) { printk(KERN_ERR "kobject '%s' (%p): tried to add an " "uninitialized object, something is seriously wrong.\n", kobject_name(kobj), kobj); dump_stack(); return -EINVAL; } va_start(args, fmt); retval = kobject_add_varg(kobj, parent, fmt, args);//主要的add操作 va_end(args); return retval; </span>kobject_add_varg():
static int kobject_add_varg(struct kobject *kobj, struct kobject *parent, const char *fmt, va_list vargs) { int retval; retval = kobject_set_name_vargs(kobj, fmt, vargs);//设置kobject的名字。即kobject的name成员 if (retval) { printk(KERN_ERR "kobject: can not set name properly!\n"); return retval; } kobj->parent = parent; //设置kobject的parent return kobject_add_internal(kobj);//在sysfs中添加kobjcet信息 } </span>
static int kobject_add_internal(struct kobject *kobj) { int error = 0; struct kobject *parent; if (!kobj) return -ENOENT; if (!kobj->name || !kobj->name[0]) { //如果kobject的名字为空.退出 WARN(1, "kobject: (%p): attempted to be registered with empty " "name!\n", kobj); return -EINVAL; } parent = kobject_get(kobj->parent);//如果parent为真,则增加kobj->kref计数,也就是父节点的引用计数 /* 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);//如果kobj-parent父节点为NULL那么就用kobj->kset->kobj作其父节点,并增加其引用计数 kobj_kset_join(kobj);//把kobj的entry成员添加到kobj->kset>list的尾部,现在的层次就是/kobj->kset->list指向kobj->parent ->parent 指向kset->kobj kobj->parent = 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>"); error = create_dir(kobj); //利用kobj创建目录和属性文件,其中会判断,如果parent为NULL那么就在sysfs_root下创建 if (error) { //如果创建失败。减少相关的引用计数 kobj_kset_leave(kobj); kobject_put(parent); kobj->parent = NULL; /* be noisy on error issues */ if (error == -EEXIST) printk(KERN_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)); else printk(KERN_ERR "%s failed for %s (%d)\n", __func__, kobject_name(kobj), error); dump_stack(); } else kobj->state_in_sysfs = 1; //如果创建成功。将state_in_sysfs建为1。表示该object已经在sysfs中了 return error; }
static int create_dir(struct kobject *kobj) { int error = 0; if (kobject_name(kobj)) { error = sysfs_create_dir(kobj);//为kobject创建目录 if (!error) { error = populate_dir(kobj); //为kobject->ktype中的属性创建文件 if (error) sysfs_remove_dir(kobj); } } return error; } </span>我们先看一下kobject所表示的目录创建过程。这是在sysfs_create_dir()中完成的
int sysfs_create_dir(struct kobject * kobj) { enum kobj_ns_type type; struct sysfs_dirent *parent_sd, *sd; const void *ns = NULL; int error = 0; BUG_ON(!kobj); /* 如果kobject的parnet存在。就在目录点的目录下创建这个目录。 如果没有父结点不存在,就在/sys下面创建结点 */ if (kobj->parent) parent_sd = kobj->parent->sd; else parent_sd = &sysfs_root; if (sysfs_ns_type(parent_sd)) ns = kobj->ktype->namespace(kobj); type = sysfs_read_ns_type(kobj); //在sysfs中创建目录 error = create_dir(kobj, parent_sd, type, ns, kobject_name(kobj), &sd); if (!error) kobj->sd = sd; return error; } </span>接着看为kobject->ktype中的属性创建文件。这是在populate_dir()中完成的
static int populate_dir(struct kobject *kobj) { struct kobj_type *t = get_ktype(kobj); struct attribute *attr; int error = 0; int i; if (t && t->default_attrs) { for (i = 0; (attr = t->default_attrs[i]) != NULL; i++) {//遍历ktype中的属性,为其建立文 error = sysfs_create_file(kobj, attr); //注意:文件的操作最后都会回溯到ktype->sysfs_ops的show和store这两个函数中. if (error) break; } } return error; }另外一个常用的函数就是kobject_init_and_add,直接初始化并进行添加操作
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; }这里面调用的也就是init和add操作了。
还有一个kobj的删除函数:
void kobject_del(struct kobject *kobj) { if (!kobj) return; sysfs_remove_dir(kobj);//删除sys目录相关文件 kobj->state_in_sysfs = 0; kobj_kset_leave(kobj);//kset链表中删除kobj成员 kobject_put(kobj->parent);//减少parent计数 kobj->parent = NULL; }
基于 linux2.6.32.2 的驱动
#include <linux/device.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/string.h> #include <linux/sysfs.h> #include <linux/stat.h> MODULE_AUTHOR("David Xie"); MODULE_LICENSE("Dual BSD/GPL"); /*声明release、show、store函数*/ void obj_test_release(struct kobject *kobject); ssize_t kobj_test_show(struct kobject *kobject, struct attribute *attr,char *buf); ssize_t kobj_test_store(struct kobject *kobject,struct attribute *attr,const char *buf, size_t count); /*对应于kobject的目录下的一个文件,Name成员就是文件名*/ struct attribute test_attr = { .name = "kobj_config", .mode = S_IRWXUGO, }; static struct attribute *def_attrs[] = { &test_attr, NULL, }; //kobject对象的操作 struct sysfs_ops obj_test_sysops = { .show = kobj_test_show, .store = kobj_test_store, }; /*定义kobject对象的一些属性及对应的操作*/ struct kobj_type ktype = { .release = obj_test_release, .sysfs_ops=&obj_test_sysops, .default_attrs=def_attrs, }; /*release方法释放该kobject对象*/ void obj_test_release(struct kobject *kobject) { printk("eric_test: release .\n"); } /*当读文件时执行的操作*/ ssize_t kobj_test_show(struct kobject *kobject, struct attribute *attr,char *buf) { printk("have show.\n"); printk("attrname:%s.\n", attr->name); sprintf(buf,"%s\n",attr->name); return strlen(attr->name)+2; } /*当写文件时执行的操作*/ ssize_t kobj_test_store(struct kobject *kobject,struct attribute *attr,const char *buf, size_t count) { printk("havestore\n"); printk("write: %s\n",buf); return count; } struct kobject kobj;//声明kobject对象 static int kobj_test_init(void) { printk("kboject test init.\n"); kobject_init_and_add(&kobj,&ktype,NULL,"kobject_test");//初始化kobject对象kobj,并将其注册到linux系统 //kobject_init(&kobj); //kobj.ktype = &ktype; //kobj.parent = NULL; //kobject_set_name(&kobj, "kobject_test"); //err = kobject_add(&kobj); return 0; } static void kobj_test_exit(void) { printk("kobject test exit.\n"); kobject_del(&kobj); } module_init(kobj_test_init); module_exit(kobj_test_exit);
insmod kobject.ko
ls /sys/
可以看到新增了一个 kobject_test
ls /sys/kobject
可以看到 kobj_config
cat /sys/kobject_test/kobj_config