不同的结构包含kobject后,kobject的属性会不同,kobject销毁时所做的操作会不同,kobject所表现出的类型也会不同。所以,kobject中包含了一个叫作kobj_type的结构。kobj_type的目标就是为不同类型的kobject提供不同的属性以及销毁方法。
kobj_type与kobject的关系比较简单,是一种明显的依存关系,正如价值因为人的存在而产生意义并发挥作用一样,kobj_type本身虽然包含了做什么(但未必包括怎么做,且听下文分析),但若没有了kobject,kobj_type也无法发挥作用,就像一条截断的手臂,它是真实存在那里,但却无法独立运动。同样地,这也证明了缺手缺角确实要比脑残好得多。用一种粗俗不求深刻的眼光去审视kobject_init_and_add源码,可以看到这么一句话kobject_init(kobj, ktype),正是媒婆kobject_init_and_add的这么一句话,kytpe嫁给了kobj,从此这个ktype只和这个kobj关联,当然离婚再嫁也是可以的。
ksets 也有一个指针指向 kobj_type 结构来描述它包含的 kobject,这个类型优先于 kobject 自身中的 ktype 。因此在典型的应用中, 在 structkobject 中的 ktype 成员被设为 NULL, 而 kset 中的ktype是实际被使用的。
看一下kobj_type的结构:
struct kobj_type { void (*release)(struct kobject *kobj);//用于释放kobject占用的资源 const struct sysfs_ops *sysfs_ops;/*提供实现以下属性的方法*/ struct attribute **default_attrs; /*用于保存类型属性列表(指针的指针) */ const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj); const void *(*namespace)(struct kobject *kobj); };
struct attribute { const char *name;/*属性的名字( 在 kobject 的 sysfs 目录中显示)*/ struct module *owner;/*指向模块的指针(如果有), 此模块负责实现这个属性*/ mode_t mode;/*属性的保护位,modes 的宏定义在 <linux/stat.h>:例如S_IRUGO 为只读属性等等*/ #ifdef CONFIG_DEBUG_LOCK_ALLOC struct lock_class_key *key; struct lock_class_key skey; #endif };
struct sysfs_ops { ssize_t (*show)(struct kobject *, struct attribute *,char *);//数据读取 ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t);//数据保存 };每一个attribute会对应自己的show/store函数,show方法实现数据读取。当用户空间读取属性值时,核心调用该方法实现编码,结果存放在形参buf中,注意大小不能超过过PAGE_SIZE。
store 方法实现数据保存。当用户控件设置属性值时,核心调用该方法实现解码,使用buf传递的数据解码,count指示buf传递的数据长度。注意buf信息来 自用户空间,因此在解码前应当检测数据合法性,如果数据格式或者数值和期望的不符,应该返回一个负的错误码,而不是采取不可预期或者无法恢复的动作。
另外一个需要注意的是,对于store方法,不能返回0,否则会产生死循环。因为如果store返回小于形参count,驱动核心会认为解码未完成,并以本次解码剩余的缓冲区继续调用store。我们假设一个最多一次只能解码4个字符的store函数,见如下代码调用
const char *p=”1234567890”;
attrs->store(bus,p,10);
第一次返回5,驱动核心会接着调用:
attrs->store(bus,p+4,10-4);
attrs->store(bus,p+8,10-8);
…
持续直到count为0。因此一个返回0的store会导致永久循环。
当用户空间读取一个属性时(如:#cat dev),内核会使用指向 kobject 的指针(kobj)和正确的属性结构(*attr)来调用show 方法,该方法将给定属性值编码进缓冲(buffer)(注意不要越界( PAGE_SIZE 字节)), 并返回实际数据长度。
也可对所有 kobject (通常指在一个kset关联的范围内)关联的属性使用同一个 show 方法,用传递到函数的 attr 指针来判断所请求的属性。有的 show 方法包含对属性名字的检查。有的show 方法会将属性结构嵌入另一个结构(本文举的例子就是用的这种方法), 这个结构包含需要返回属性值的信息,这时可用container_of 获得上层结构的指针以返回属性值的信息。
当用户空间写入一个属性时(如echo “hello” > event)内核会使用指向 kobject 的指针(kobj)和正确的属性结构(*attr)来调用store 方法。
store 方法将存在缓冲(buffer)的数据( size为数据的长度,不能超过 PAGE_SIZE )解码并保存新值到属性(*attr), 返回实际解码的字节数。store 方法只在拥有属性的写权限时才能被调用。此时注意:接收来自用户空间的数据一定要验证其合法性。如果到数据不匹配, 返回一个负的错误值。
每一个 ktype需要有一个关联的 kobj_type 结构,指向这个结构的指针能在 2 个不同的地方找到:
(1)kobject 结构自身包含一个成员(ktype)指向kobj_type ;
struct kobject { …… struct kobj_type * ktype;/*负责对该kobject类型进行跟踪的struct kobj_type的指针*/ …… }(2)如果这个 kobject 是一个 kset 的成员, kset 会提供kobj_type 指针。
struct kset { struct kobj_type * ktype; /*指向该kset对象类型的指针*/ …… }访问属性的时候到底是用的哪个kobj_type呢?我们可以看下sysfs的系统调用read函数s。
static ssize_t sysfs_read_file(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct sysfs_buffer * buffer = file->private_data; ssize_t retval = 0; mutex_lock(&buffer->mutex); if (buffer->needs_read_fill || *ppos == 0) { retval = fill_read_buffer(file->f_path.dentry,buffer); if (retval) goto out; } pr_debug("%s: count = %zd, ppos = %lld, buf = %s\n", __func__, count, *ppos, buffer->page); retval = simple_read_from_buffer(buf, count, ppos, buffer->page, buffer->count); out: mutex_unlock(&buffer->mutex); return retval; }注意这里调用了fill_read_buffer,传递的有一个buffer,而这里的buffer是file->private_data.我们看下fill_read_buffer,
static int fill_read_buffer(struct dentry * dentry, struct sysfs_buffer * buffer) { struct sysfs_dirent *attr_sd = dentry->d_fsdata; struct kobject *kobj = attr_sd->s_parent->s_dir.kobj; const struct sysfs_ops * ops = buffer->ops; int ret = 0; ssize_t count; ..... count = ops->show(kobj, attr_sd->s_attr.attr, buffer->page); .... }这里调用了ops->show,ops则是buffer的ops成员,那么这个buffer是什么时候赋值的呢,其实就是在我们调用open的时候
static int sysfs_open_file(struct inode *inode, struct file *file) { struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; struct kobject *kobj = attr_sd->s_parent->s_dir.kobj; struct sysfs_buffer *buffer; const struct sysfs_ops *ops; int error = -EACCES; char *p; ...... /* every kobject with an attribute needs a ktype assigned */ if (kobj->ktype && kobj->ktype->sysfs_ops) ops = kobj->ktype->sysfs_ops; else { WARN(1, KERN_ERR "missing sysfs attribute operations for " "kobject: %s\n", kobject_name(kobj)); goto err_out; } ...... error = -ENOMEM; buffer = kzalloc(sizeof(struct sysfs_buffer), GFP_KERNEL); if (!buffer) goto err_out; mutex_init(&buffer->mutex); buffer->needs_read_fill = 1; buffer->ops = ops; file->private_data = buffer; ...... }可以看到这里的private_data赋值为buffer,而ops又赋值给了buffer,ops的值是kobj->ktype->sysfs_ops。
所以我们在sysfs里显示属性调用的show函数应该是它的ktype中的。
跟attribute相关的结构还有一个kobj_attribute
struct kobj_attribute { struct attribute attr; ssize_t (*show)(struct kobject *kobj, struct kobj_attribute *attr, char *buf); ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count); };
另外还有一个要注意下的就是release,函数指针release是给kref使用的,当引用计数为0这个指针指向的函数会被调用来释放内存
ktype的知识也就上面差不多了。