linux设备驱动模型一三基础结构之Ktype

不同的结构包含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_tmode;/*属性的保护位,modes 的宏定义在 <linux/stat.h>:例如S_IRUGO 为只读属性等等*/#ifdef CONFIG_DEBUG_LOCK_ALLOCstruct lock_class_key*key;struct lock_class_keyskey;#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_tsysfs_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);};

可以看到,这个结构就是structattribute和struct sysfs_ops的一个综合,具体有什么用呢,我们后面会时行 实例分析

另外还有一个要注意下的就是release,函数指针release是给kref使用的,当引用计数为0这个指针指向的函数会被调用来释放内存

ktype的知识也就上面差不多了。

赶快上路吧,不要有一天我们在对方的葬礼上说,要是当时去了就好了。

linux设备驱动模型一三基础结构之Ktype

相关文章:

你感兴趣的文章:

标签云: