1.申请ID:腾度科技
2.邮箱:3190550272@qq.com
3.技术文章
关于:linux-sysfs
一、inode、dentry、sys_dirent、kobject他们分别都有自己的一棵树,inode和dentry就是vfs用的inode和dentry,sysfs之所以创建这两个结构就是为了与vfs交互。至于sys_dirent和kobject是sysfs维护信息用的,sys_dirent可以对应到文件,而kobject只能对应到对象(目录),其实sys_dirent比较简单,也就是个辅助结构
struct sysfs_dirent {
atomic_t s_count;/*引用计数*/
struct list_heads_sibling;
struct list_heads_children;
void * s_element;/*对应于某一类型的数据结构,比如如果该结构类型是目录,则它对应一个kobject,如果是属性文件,它对应一个attribute结构*/
int s_type;/*类型: 包括目录、根、属性文件、二进制文件
链接*/
umode_t s_mode;
ino_t s_ino;
struct dentry * s_dentry;
struct iattr * s_iattr;
atomic_t s_event;
};
靠s_element来与kobject建立关系,而还是通过s_element可以携带具体文件的信息,弥补了kobject无法对应的文件的缺陷。而dentry->d_fsdata = sysfs_dirent ,这样sysfs_dirent 和dentry就建立了关系,从而dentry和kobject也建立了关系,而且从dentry也可得到具体文件的信息了。二、sysfs文件的操作:统一接口sysfs_file_operations:const struct file_operations sysfs_file_operations = {
.read = sysfs_read_file,
.write = sysfs_write_file,
.llseek = generic_file_llseek,
.open = sysfs_open_file,
.release = sysfs_release,
.poll = sysfs_poll,
};
以sysfs_read_file为例,调用关系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;
down(&buffer->sem);
if (buffer->needs_read_fill) {
if (buffer->orphaned)
retval = -ENODEV;
else
retval = fill_read_buffer(file->f_path.dentry,buffer);
if (retval)
goto out;
}
pr_debug("%s: count = %zd, ppos = %lld, buf = %s\n",
__FUNCTION__, count, *ppos, buffer->page);
retval = simple_read_from_buffer(buf, count, ppos, buffer->page,
buffer->count);
out:
up(&buffer->sem);
return retval;
}
static int fill_read_buffer(struct dentry * dentry, struct sysfs_buffer * buffer)
{
struct sysfs_dirent * sd = dentry->d_fsdata;
struct attribute * attr = to_attr(dentry);(static inline struct attribute * to_attr(struct dentry * dentry){
struct sysfs_dirent * sd = dentry->d_fsdata;
return ((struct attribute *) sd->s_element);
}attr是show函数的参数,通过他找到具体文件的属性,从而找到具体的show函数。)
struct kobject * kobj = to_kobj(dentry->d_parent);
struct sysfs_ops * ops = buffer->ops;(static int sysfs_open_file(struct inode *inode, struct file *file)
{
...... buffer = kzalloc(sizeof(struct sysfs_buffer), GFP_KERNEL);
if (buffer) {
INIT_LIST_HEAD(&buffer->associates);
init_MUTEX(&buffer->sem);
buffer->needs_read_fill = 1;
buffer->ops = ops;(后面会讲到)
add_to_collection(buffer, inode);
file->private_data = buffer;
} else
error = -ENOMEM;
goto Done;
}sysfs_buffer是file的private_data结构,维护着具体文件的操作以及操作所用到的内存信息struct sysfs_buffer {
struct list_headassociates;
size_t count;
loff_t pos;
char * page;
struct sysfs_ops* ops;
struct semaphoresem;
int orphaned;
int needs_read_fill;
int event;
};
)
int ret = 0;
ssize_t count;
if (!buffer->page)
buffer->page = (char *) get_zeroed_page(GFP_KERNEL);
if (!buffer->page)
return -ENOMEM;
buffer->event = atomic_read(&sd->s_event);
count = ops->show(kobj,attr,buffer->page);
BUG_ON(count > (ssize_t)PAGE_SIZE);
if (count >= 0) {
buffer->needs_read_fill = 0;
buffer->count = count;
} else {
ret = count;
}
return ret;
}
sysfs_read_file --- > ops->show(该show函数是kobject里ktype的sysfs_ops,在sysfs_open_file中可以查到 ,)static int sysfs_open_file(struct inode *inode, struct file *file)
{
struct kobject *kobj = sysfs_get_kobject(file->f_path.dentry->d_parent);
struct attribute * attr = to_attr(file->f_path.dentry);
struct sysfs_buffer_collection *set;
struct sysfs_buffer * buffer;
struct sysfs_ops * ops = NULL;
int error = 0;
if (!kobj || !attr)
goto Einval;
/* Grab the module reference for this attribute if we have one */
if (!try_module_get(attr->owner)) {
error = -ENODEV;
goto Done;
}
/* if the kobject has no ktype, then we assume that it is a subsystem
* itself, and use ops for it.
*/
if (kobj->kset && kobj->kset->ktype)
ops = kobj->kset->ktype->sysfs_ops;
else if (kobj->ktype)
ops = kobj->ktype->sysfs_ops;
else
ops = &subsys_sysfs_ops;... ...
而sysfs_ops从何而来呢,他是在创建具体的kobject时被赋值的,以cpufreq为例:cpufreq_add_dev函数中有policy->kobj.ktype = &ktype_cpufreq;
strlcpy(policy->kobj.name, "cpufreq", KOBJ_NAME_LEN);
ret = kobject_register(&policy->kobj);static struct kobj_type ktype_cpufreq = {
.sysfs_ops= &sysfs_ops,
.default_attrs= default_attrs,
.release = cpufreq_sysfs_release,
};
static struct sysfs_ops sysfs_ops = {
.show =show,
.store = store,
};
static ssize_t show(struct kobject * kobj, struct attribute * attr ,char * buf)
{
struct cpufreq_policy * policy = to_policy(kobj);
struct freq_attr * fattr = to_attr(attr);
ssize_t ret;
policy = cpufreq_cpu_get(policy->cpu);
if (!policy)
return -EINVAL;
if (lock_policy_rwsem_read(policy->cpu) < 0)
return -EINVAL;
if (fattr->show)
ret = fattr->show(policy, buf);
else
ret = -EIO;
unlock_policy_rwsem_read(policy->cpu);
cpufreq_cpu_put(policy);
return ret;
}
to_attr 是通过一般属性找到特定于某个对象文件的属性,看懂__ATTR这个宏就明白了。之所以层层调用组要是linux喜欢把公用的部分提取出来组成个函数,私有部分再另组成个函数。三、sysfs_create_file该函数创建sysfs文件,就是创建一个sys_dirent的结构。注意这里木有创建inode和dentry,文件的inode和dentry是被访问前创建的,既lookup时创建的static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry,
struct nameIDAta *nd)
{
struct sysfs_dirent * parent_sd = dentry->d_parent->d_fsdata;
struct sysfs_dirent * sd;
int err = 0;
/**
#define list_for_each_entry(pos, head, member) \
for (pos = list_entry((head)->next, typeof(*pos), member);\
prefetch(pos->member.next), &pos->member != (head);\
pos = list_entry(pos->member.next, typeof(*pos), member))
**/
list_for_each_entry(sd, &parent_sd->s_children, s_sibling) {
if (sd->s_type & SYSFS_NOT_PINNED) {/*hgq 这里判断是不是文件,如果是才往下进行,因为对于目录kobject,他在创建时就顺带创建了
dentry和inode,在sysfs_create_dir函数里,所以在内存中已经有了,也就不会调到这个函数,这个函数是内存里没有相应的
dentry和inode时才调用,在这里就是创建sysfs的文件的dentry和inode,因为创建文件时没有创建
dentry和inode,只是创建了sysfs_dirent*/
const unsigned char * name = sysfs_get_name(sd);
if (strcmp(name, dentry->d_name.name))
continue;
if (sd->s_type & SYSFS_KOBJ_LINK)
err = sysfs_attach_link(sd, dentry);
else
err = sysfs_attach_attr(sd, dentry);
break;
}
}
}linux就是用时才去做一些事,以此可以节省空间。 |