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_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);
};

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

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

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



你可能感兴趣的:(linux,struct,File,Class,buffer)