zoukankan      html  css  js  c++  java
  • sysfs实例

    转自:http://blog.chinaunix.net/u1/51562/showart_1076295.html

    :前言

    在设备模型中,sysfs文件系统用来表示设备的结构.将设备的层次结构形象的反应到用户空间中.用户空间 

    可以修改sysfs中的文件属性来修改设备的属性值,今天我们就来详细分析一下,sysfs的实现.

    :sysfs的初始化和挂载

    Sysfs文件系统的初始化是在sysfs_init()中完成的,代码如下:

    int __init sysfs_init(void)

    {

       int err = -ENOMEM;

    //创建一个分配sysfs_direntcache

    //name: sysfs_dir_cache cache/proc/slabinfo中显示的名字

           sysfs_dir_cachep = kmem_cache_create("sysfs_dir_cache",

                                             sizeof(struct sysfs_dirent),

                                             0, 0, NULL);

           if (!sysfs_dir_cachep)

                  goto out;

           err = sysfs_inode_init();

           if (err)

                  goto out_err;

           //注册sysfs文件系统s

           err = register_filesystem(&sysfs_fs_type);

           if (!err) {

                  //挂载sysfs文件系统

                  sysfs_mount = kern_mount(&sysfs_fs_type);

                  if (IS_ERR(sysfs_mount)) {

                         printk(KERN_ERR "sysfs: could not mount! ");

                         err = PTR_ERR(sysfs_mount);

                         sysfs_mount = NULL;

                         unregister_filesystem(&sysfs_fs_type);

                         goto out_err;

                  }

           } else

                  goto out_err;

    out:

           return err;

    out_err:

           kmem_cache_destroy(sysfs_dir_cachep);

           sysfs_dir_cachep = NULL;

           goto out;

    }

    每个kobject对应sysfs中的一个目录,kobject的每个属性对应sysfs文件系统中的文件.

    struct sysfs_dirent就是用来做kobjectdentry的互相转换用的.它们的关系如下图所示:

    上图表示的是一个kobject的层次结构.dentryd_fsdata字段指定该结点所表示的 

    sysfs_dirent.sysfs_dirent.s_parent表示它的父kobject. sysfs_dirent.s_sibling表示它的兄弟结点 

    sysfs_dirent.s_dir.children表示它所属的子节点.

    从上图可知.如果要遍历一个结点下面的子结点,只需要找到sysfs_dirent.s_dir.children结点

    然后按着子节点的s_sibling域遍历即可.

    当然,有时候也需要从struct sysfs_dirent导出它所属的dentry结点.我们在代码中遇到的时候再进行分 

    .

    Sysfs文件系统的file_system_type定义如下:

    static struct file_system_type sysfs_fs_type = {

           .name           = "sysfs",

           .get_sb          = sysfs_get_sb,

           .kill_sb    = kill_anon_super,

    };

    通过前面文件系统的相关分析,我们知道在sys_mount()中最终会调用struct file_system_typeget_sb 

    函数来实现文件系统的挂载.它的代码如下:

    static int sysfs_get_sb(struct file_system_type *fs_type,

           int flags, const char *dev_name, void *data, struct vfsmount *mnt)

    {

           return get_sb_single(fs_type, flags, data, sysfs_fill_super, mnt);

    }

    get_sb_single()的代码在前面已经涉及到,它对super_block.以及挂载的dentryinode的赋值是在回调 

    函数sysfs_fill_super, mnt()中完成的.代码如下:

    static int sysfs_fill_super(struct super_block *sb, void *data, int silent)

    {

           struct inode *inode;

           struct dentry *root;

           sb->s_blocksize = PAGE_CACHE_SIZE;

           sb->s_blocksize_bits = PAGE_CACHE_SHIFT;

           sb->s_magic = SYSFS_MAGIC;

           sb->s_op = &sysfs_ops;

           sb->s_time_gran = 1;

           sysfs_sb = sb;

           /* get root inode, initialize and unlock it */

           inode = sysfs_get_inode(&sysfs_root);

           if (!inode) {

                  pr_debug("sysfs: could not get root inode ");

                  return -ENOMEM;

           }

           /* instantiate and link root dentry */

           root = d_alloc_root(inode);

           if (!root) {

                  pr_debug("%s: could not get root dentry! ",__FUNCTION__);

                  iput(inode);

                  return -ENOMEM;

           }

           //sysfs_root关联到root

           root->d_fsdata = &sysfs_root;

           sb->s_root = root;

           return 0;

    }

    在这里要注意几个全局量. sysfs_sb表示sysfs文件系统的super_block. sysfs_root表示sysfs文件系统 

    根目录的struct sysfs_dirent.

    sysfs_get_inode(&sysfs_root)用来将sysfs_root导出相应的inode.代码如下:

    struct inode * sysfs_get_inode(struct sysfs_dirent *sd)

    {

           struct inode *inode;

           //super_blocksd->s_ino为哈希值,到哈希表中寻找相应的inode.如果不存在,则新建

           inode = iget_locked(sysfs_sb, sd->s_ino);

           //对新生成的inode进行初始化

           if (inode && (inode->i_state & I_NEW))

                  sysfs_init_inode(sd, inode);

           return inode;

    }

    首先,它以sysfs文件系统的super_blockstruct sysfs_dirent.s_ino成员的值做为哈希值到哈希表中 

    寻找相应的inode.如果在哈希表中不存在这个inode,那就新建一个,并将它链入到哈希表.之后,调用 

    sysfs_init_inode()对生成的inode进行初始化.显然.mount的时候是不会生成inode.必定会进入 

    sysfs_init_inode()函数.代码如下:

    static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode)

    {

           struct bin_attribute *bin_attr;

           inode->i_blocks = 0;

           inode->i_mapping->a_ops = &sysfs_aops;

           inode->i_mapping->backing_dev_info = &sysfs_backing_dev_info;

           inode->i_op = &sysfs_inode_operations;

           inode->i_ino = sd->s_ino;

           lockdep_set_class(&inode->i_mutex, &sysfs_inode_imutex_key);

           if (sd->s_iattr) {

                  /* sysfs_dirent has non-default attributes

                   * get them for the new inode from persistent copy

                   * in sysfs_dirent

                   */

                  set_inode_attr(inode, sd->s_iattr);

           } else

                  set_default_inode_attr(inode, sd->s_mode);

           /* initialize inode according to type */

           switch (sysfs_type(sd)) {

           case SYSFS_DIR:

                  inode->i_op = &sysfs_dir_inode_operations;

                  inode->i_fop = &sysfs_dir_operations;

                  inode->i_nlink = sysfs_count_nlink(sd);

                  break;

           case SYSFS_KOBJ_ATTR:

                  inode->i_size = PAGE_SIZE;

                  inode->i_fop = &sysfs_file_operations;

                  break;

           case SYSFS_KOBJ_BIN_ATTR:

                  bin_attr = sd->s_bin_attr.bin_attr;

                  inode->i_size = bin_attr->size;

                  inode->i_fop = &bin_fops;

                  break;

           case SYSFS_KOBJ_LINK:

                  inode->i_op = &sysfs_symlink_inode_operations;

                  break;

           default:

                  BUG();

           }

           unlock_new_inode(inode);

    }

    在这里,我们可以看到sysfs文件系统中的各种操作函数了..

    syfs文件系统中,怎么样判断一个目录下是否有这个文件呢?

    在前面有关文件系统的分析中我们可以看.有关文件的查找实际上都会由inod->i_op-> lookup()函数进行 

    判断.sysfs,这个函数对应为sysfs_lookup().代码如下:

    static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry,

                                struct nameidata *nd)

    {

           struct dentry *ret = NULL;

           //取得父结点对应的sysfs_dirent

           struct sysfs_dirent *parent_sd = dentry->d_parent->d_fsdata;

           struct sysfs_dirent *sd;

           struct inode *inode;

           mutex_lock(&sysfs_mutex);

           //父结点的sysfs_dirent中是否有相应的子结点

           sd = sysfs_find_dirent(parent_sd, dentry->d_name.name);

           /* no such entry */

           //如果没有.这个结点是不存在的

           if (!sd) {

                  ret = ERR_PTR(-ENOENT);

                  goto out_unlock;

           }

           /* attach dentry and inode */

    //如果有这个结点,为之生成inod结构.

           inode = sysfs_get_inode(sd);

           if (!inode) {

                  ret = ERR_PTR(-ENOMEM);

                  goto out_unlock;

           }

           /* instantiate and hash dentry */

           dentry->d_op = &sysfs_dentry_ops;

           //关联dentrysysfs_dirent

           dentry->d_fsdata = sysfs_get(sd);

           d_instantiate(dentry, inode);

           d_rehash(dentry);

     out_unlock:

           mutex_unlock(&sysfs_mutex);

           return ret;

    }

    由此可见,它的判断会转入到相应的sysfs_dirent中进行判断.如果设备模型在创建目录/文件的时候并不 

    会创建dentry或者inode.只会操作sysfs_dirent结构如果找到了这个结构,就为这个结构生成inode. 

    将其关联到denry.

    sysfs_find_dirent()如下:

    struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd,

                                       const unsigned char *name)

    {

           struct sysfs_dirent *sd;

           for (sd = parent_sd->s_dir.children; sd; sd = sd->s_sibling)

                  if (!strcmp(sd->s_name, name))

                         return sd;

           return NULL;

    }

    它用的搜索方法就是我们在之前分析sysfs_dirent结构所讲述的.分析到这里,sysfs的大概轮廓就出现在 

    我们的眼前了.^_^.接下来分析sysfs文件系统中目录的创建过程

    :sysfs文件系统中创建目录

    linux设备模型中,每注册一个kobject.就会为之创建一个目录.具体的流程在分析linux设备模型的时候 

    再给出详细的分析.创建目录的接口为: sysfs_create_dir().代码如下:

    int sysfs_create_dir(struct kobject * kobj)

    {

           struct sysfs_dirent *parent_sd, *sd;

           int error = 0;

           BUG_ON(!kobj);

           //如果kobject没有指定父结点,则将其父结点指定为sysfs的根目录syfs_root

           if (kobj->parent)

                  parent_sd = kobj->parent->sd;

           else

                  parent_sd = &sysfs_root;

           //创建目录

           error = create_dir(kobj, parent_sd, kobject_name(kobj), &sd);

           //kobj->sd 指向对应的sysfs_dirent

           if (!error)

                  kobj->sd = sd;

           return error;

    }

    在这里,先为结点指定父目录,然后调用create_dir()在父目录下生成结点.代码如下:

    static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd,

                        const char *name, struct sysfs_dirent **p_sd)

    {

           //指定目录的模式

           umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO;

           struct sysfs_addrm_cxt acxt;

           struct sysfs_dirent *sd;

           int rc;

           /* allocate */

           //分配并初始化一个sysfs_dirent

           sd = sysfs_new_dirent(name, mode, SYSFS_DIR);

           if (!sd)

                  return -ENOMEM;

           //初始化sd->s_dir.kobj字段

           sd->s_dir.kobj = kobj;

           /* link in */

           //acxt是一个临时变量.它用来存放父结点的相关信息

           //设置acxt->parent_sd  为父结点的sysfs_dirent.acxt->parent_inode为父结点的inode

           sysfs_addrm_start(&acxt, parent_sd);

           //设置sd->s_parent.并按inod值按顺序链入父结点的children链表

           rc = sysfs_add_one(&acxt, sd);

           sysfs_addrm_finish(&acxt);

           if (rc == 0)

                  *p_sd = sd;

           else

                  sysfs_put(sd);

           return rc;

    }

    在这里,为子节点生成了对应的sysfs_dirent.设置了它的父结点域,并将其链入到父结点的children链表. 

    这样,在文件系统中查找父目录下面的子结点了.

    :sysfs中创建一般属性文件

    Kobject的每一项属性都对应在sysfs文件系统中,kobject对应的目录下的一个文件.文件名称与属性名称 

    相同.创建一般属性的接口为sysfs_create_file().代码如下:

    int sysfs_create_file(struct kobject * kobj, const struct attribute * attr)

    {

           BUG_ON(!kobj || !kobj->sd || !attr);

           //kobject->sd: kobject表示目录的struct sysfs_dirent结构

           return sysfs_add_file(kobj->sd, attr, SYSFS_KOBJ_ATTR);

    }

    最终会调用sysfs_add_file().参数attr.是要生成文件的属性值.

    int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr,

                     int type)

    {

           //文件对应的属性

           umode_t mode = (attr->mode & S_IALLUGO) | S_IFREG;

           struct sysfs_addrm_cxt acxt;

           struct sysfs_dirent *sd;

           int rc;

           //创建一个新的sysfs_dirent.对应的名称为attr->name.即属性的名称

           sd = sysfs_new_dirent(attr->name, mode, type);

           if (!sd)

                  return -ENOMEM;

           //设置属性值

           sd->s_attr.attr = (void *)attr;

           //将子结点的struct sysfs_dirent结构关链到父结点

           sysfs_addrm_start(&acxt, dir_sd);

           rc = sysfs_add_one(&acxt, sd);

           sysfs_addrm_finish(&acxt);

           if (rc)

                  sysfs_put(sd);

           return rc;

    }

    这个流程与创建目录的流程大部份相同.不相同的只是创建目录时,它的父目录为上一层结点,创建文件时, 

    它的父目录就是kobject对应的struct sysfs_dirent.

    这样,kobject对应的目录下面就可以看到这个文件了.^_^

    文件建好之后,要怎么样去读写呢?

    回忆一下,sysfs文件系统中,inode的初始化:

    static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode)

    {

           ……

           …….

           case SYSFS_KOBJ_ATTR:

           inode->i_size = PAGE_SIZE;

           inode->i_fop = &sysfs_file_operations;

           ……

    }

    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_open_file()代码如下:

    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;

           struct sysfs_ops *ops;

           int error = -EACCES;

           /* need attr_sd for attr and ops, its parent for kobj */

           if (!sysfs_get_active_two(attr_sd))

                  return -ENODEV;

           /* every kobject with an attribute needs a ktype assigned */

           //buffer->ops设置为kobj->ktype->sysfs_ops

           if (kobj->ktype && kobj->ktype->sysfs_ops)

                  ops = kobj->ktype->sysfs_ops;

           else {

                  printk(KERN_ERR "missing sysfs attribute operations for "

                         "kobject: %s ", kobject_name(kobj));

                  WARN_ON(1);

                  goto err_out;

           }

           /* File needs write support.

            * The inode's perms must say it's ok, 

            * and we must have a store method.

            */

           if (file->f_mode & FMODE_WRITE) {

                  if (!(inode->i_mode & S_IWUGO) || !ops->store)

                         goto err_out;

           }

           /* File needs read support.

            * The inode's perms must say it's ok, and we there

            * must be a show method for it.

            */

           if (file->f_mode & FMODE_READ) {

                  if (!(inode->i_mode & S_IRUGO) || !ops->show)

                         goto err_out;

           }

           /* No error? Great, allocate a buffer for the file, and store it

            * it in file->private_data for easy access.

            */

           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;

           /* make sure we have open dirent struct */

           //buffer链至attr_sd->s_attr.open链表上

           error = sysfs_get_open_dirent(attr_sd, buffer);

           if (error)

                  goto err_free;

           /* open succeeded, put active references */

           sysfs_put_active_two(attr_sd);

           return 0;

     err_free:

           kfree(buffer);

     err_out:

           sysfs_put_active_two(attr_sd);

           return error;

    }

    在这段代码中,需要注意以下几个操作,

    1:buffer链接在file-> private_data.buffer还被链接在sysfs_dirent->s_attr.open.这样.VFS通过 

    file.设备模型通过kobject->sd->s_attr.open都能找到这个要操作的 buffer

    2:buffer->ops被设置为了kobject->ktype->sysfs_ops

    文件的写操作入口如下:

    static ssize_t

    sysfs_write_file(struct file *file, const char __user *buf, size_t count, loff_t *ppos)

    {

           struct sysfs_buffer * buffer = file->private_data;

           ssize_t len;

           mutex_lock(&buffer->mutex);

           //buf中的内容copy到了buffer->page

           len = fill_write_buffer(buffer, buf, count);

           //与设备模型的交互

           if (len > 0)

                  len = flush_write_buffer(file->f_path.dentry, buffer, len);

           //更新ppos

           if (len > 0)

                  *ppos += len;

           mutex_unlock(&buffer->mutex);

           return len;

    }

    首先,调用fill_write_buffer()将用户空间传值下来的数值copybuffer->page.然后再调用 

    flush_write_buffer()与设备模型进行交互.

    Flush_wirte_buffer()代码如下:

    static int

    flush_write_buffer(struct dentry * dentry, struct sysfs_buffer * buffer, size_t count)

    {

           struct sysfs_dirent *attr_sd = dentry->d_fsdata;

           struct kobject *kobj = attr_sd->s_parent->s_dir.kobj;

           struct sysfs_ops * ops = buffer->ops;

           int rc;

           /* need attr_sd for attr and ops, its parent for kobj */

           if (!sysfs_get_active_two(attr_sd))

                  return -ENODEV;

           rc = ops->store(kobj, attr_sd->s_attr.attr, buffer->page, count);

           sysfs_put_active_two(attr_sd);

           return rc;

    }

    我们在分析open()操作的时候曾分析到.bufferopskobject->ktype->ops.在这里,它相当于调用了 

    kobject->ktype->ops->store().参数分别为:操作的kobject.文件对应的属性.写入的值和值的长度.

    Sysfs这样设计,主要是在VFS保持一个统一的接口,因为每一个kobject对应的属性值都不相同,.相应的, 

    作方法也不一样,这样,ktype中就区别开来了.

    文件的读操作

    相应接口为sysfs_read_file().代码如下:

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

           //从设备模型中将值取出.并存入buffer->page

           if (buffer->needs_read_fill) {

                  retval = fill_read_buffer(file->f_path.dentry,buffer);

                  if (retval)

                         goto out;

           }

           //buffer->page中的值copy到用户空间的buf

           pr_debug("%s: count = %zd, ppos = %lld, buf = %s ",

                   __FUNCTION__, count, *ppos, buffer->page);

           retval = simple_read_from_buffer(buf, count, ppos, buffer->page,

                                        buffer->count);

    out:

           mutex_unlock(&buffer->mutex);

           return retval;

    }

    读操作的流程刚好和写操作流程相反.它先从设备模型中取值,然后再copy到用户空间.

    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;

           struct sysfs_ops * ops = buffer->ops;

           int ret = 0;

           ssize_t count;

           if (!buffer->page)

                  buffer->page = (char *) get_zeroed_page(GFP_KERNEL);

           if (!buffer->page)

                  return -ENOMEM;

           /* need attr_sd for attr and ops, its parent for kobj */

           if (!sysfs_get_active_two(attr_sd))

                  return -ENODEV;

           buffer->event = atomic_read(&attr_sd->s_attr.open->event);

           count = ops->show(kobj, attr_sd->s_attr.attr, buffer->page);

           sysfs_put_active_two(attr_sd);

           /*

            * The code works fine with PAGE_SIZE return but it's likely to

            * indicate truncated result or overflow in normal use cases.

            */

           if (count >= (ssize_t)PAGE_SIZE) {

                  print_symbol("fill_read_buffer: %s returned bad count ",

                         (unsigned long)ops->show);

                  /* Try to struggle along */

                  count = PAGE_SIZE - 1;

           }

           if (count >= 0) {

                  buffer->needs_read_fill = 0;

                  buffer->count = count;

           } else {

                  ret = count;

           }

           return ret;

    }

    在这里,我们看到,最终会调用kobject->ktype->ops->show()方法.参数含义同写操作中是一样的.

    :sysfs中创建二进制属性文件

    二制制属性通常用于firmware .它用来更新firmware 的固件.它的接口为sysfs_create_bin_file()

    代码如下:

    int sysfs_create_bin_file(struct kobject * kobj, struct bin_attribute * attr)

    {

           BUG_ON(!kobj || !kobj->sd || !attr);

           return sysfs_add_file(kobj->sd, &attr->attr, SYSFS_KOBJ_BIN_ATTR);

    }

    Sysfs_add_file()这个函数我们在之前已经分析过.在这个地方,可能会引起迷糊.因为在sysfs_add_file 

    ().:

    int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr,

                     int type)

    {

           ……

           sd->s_attr.attr = (void *)attr;

           ……

    }

    这里为什么是sd->a_attr?应该是sd-> s_bin_attr才对吧!

    仔细观察struct sysfs_dirent的结构,如下:

    struct sysfs_dirent {

           atomic_t         s_count;

           atomic_t         s_active;

           struct sysfs_dirent  *s_parent;

           struct sysfs_dirent  *s_sibling;

           const char             *s_name;

           union {

                  struct sysfs_elem_dir            s_dir;

                  struct sysfs_elem_symlink    s_symlink;

                  struct sysfs_elem_attr           s_attr;

                  struct sysfs_elem_bin_attr     s_bin_attr;

           };

           unsigned int           s_flags;

           ino_t                     s_ino;

           umode_t                s_mode;

           struct iattr             *s_iattr;

    };

    注意中间是一个union 结构,实际上只占用一个内存空间.而且s_attrs_bin_arre的第一个属性都为 

    struct attribute.所以在这里, sd->a_attrsd-> s_bin_attr;的效果是一样的.内核这样处理,又少用 

    了一个接口.看来作者在设计的时候,花了很多的心思.

    二进制的文件读写与普通属性的文件读写方式大部份都一样,所不同的是.二进制文件的读写接口分别是 

    sysfs_dirent ->s_bin_attr.bin_attr->readsysfs_dirent ->s_bin_attr.bin_attr->write

    :sysfs文件系统中的链接文件

    创建链接文件的接口为: sysfs_create_link().代码如下:

    int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char * name)

    {

           struct sysfs_dirent *parent_sd = NULL;

           struct sysfs_dirent *target_sd = NULL;

           struct sysfs_dirent *sd = NULL;

           struct sysfs_addrm_cxt acxt;

           int error;

           BUG_ON(!name);

           if (!kobj)

                  parent_sd = &sysfs_root;

           else

                  parent_sd = kobj->sd;

           error = -EFAULT;

           if (!parent_sd)

                  goto out_put;

           /* target->sd can go away beneath us but is protected with

            * sysfs_assoc_lock.  Fetch target_sd from it.

            */

           spin_lock(&sysfs_assoc_lock);

           if (target->sd)

                  target_sd = sysfs_get(target->sd);

           spin_unlock(&sysfs_assoc_lock);

           error = -ENOENT;

           if (!target_sd)

                  goto out_put;

           error = -ENOMEM;

           sd = sysfs_new_dirent(name, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK);

           if (!sd)

                  goto out_put;

           sd->s_symlink.target_sd = target_sd;

           target_sd = NULL; /* reference is now owned by the symlink */

           sysfs_addrm_start(&acxt, parent_sd);

           error = sysfs_add_one(&acxt, sd);

           sysfs_addrm_finish(&acxt);

           if (error)

                  goto out_put;

           return 0;

     out_put:

           sysfs_put(target_sd);

           sysfs_put(sd);

           return error;

    }

    上面的操作大部份都与普通文件的创建相似,所不同的只是下面这段代码的区别:

    sd->s_symlink.target_sd = target_sd;

    就是在sd->s_symlink.target_sd保存到链接目的地的sysfs_dirent.

    符号链接的操作如下所示:

    const struct inode_operations sysfs_symlink_inode_operations = {

           .readlink = generic_readlink,

           .follow_link = sysfs_follow_link,

           .put_link = sysfs_put_link,

    };

    在通过符号链接查找文件的时候,VFS中会调用inod->i_op->.readlink()进行操作.它的代码如下:

    int generic_readlink(struct dentry *dentry, char __user *buffer, int buflen)

    {

           struct nameidata nd;

           void *cookie;

           nd.depth = 0;

           cookie = dentry->d_inode->i_op->follow_link(dentry, &nd);

           if (!IS_ERR(cookie)) {

                  int res = vfs_readlink(dentry, buffer, buflen, nd_get_link(&nd));

                  if (dentry->d_inode->i_op->put_link)

                         dentry->d_inode->i_op->put_link(dentry, &nd, cookie);

                  cookie = ERR_PTR(res);

           }

           return PTR_ERR(cookie);

    }

    它的操作和其它文件系统一样,都是通用follow_link()取得目的地的路径.然后保存到nd->saved_names[] 

    ,然后,调用vfs_readlink()将目标路径copybuffer.接着,调用put_link进行事后处理工作.

    Follow_link()的操作如下示:

    static void *sysfs_follow_link(struct dentry *dentry, struct nameidata *nd)

    {

           int error = -ENOMEM;

           unsigned long page = get_zeroed_page(GFP_KERNEL);

           if (page)

                  error = sysfs_getlink(dentry, (char *) page); 

           nd_set_link(nd, error ? ERR_PTR(error) : (char *)page);

           return NULL;

    }

    Ne_set_link()是将page中的值copynd->saved_name[].

    sysfs_getlink()的代码如下:

    sysfs_getlink()-àsysfs_get_target_path()

    static int sysfs_get_target_path(struct sysfs_dirent *parent_sd,

                                 struct sysfs_dirent *target_sd, char *path)

    {

           struct sysfs_dirent *base, *sd;

           char *s = path;

           int len = 0;

           /* go up to the root, stop at the base */

           base = parent_sd;

           while (base->s_parent) {

                  sd = target_sd->s_parent;

                  while (sd->s_parent && base != sd)

                         sd = sd->s_parent;

                  if (base == sd)

                         break;

                  strcpy(s, "../");

                  s += 3;

                  base = base->s_parent;

           }

           /* determine end of target string for reverse fillup */

           sd = target_sd;

           while (sd->s_parent && sd != base) {

                  len += strlen(sd->s_name) + 1;

                  sd = sd->s_parent;

           }

           /* check limits */

           if (len < 2)

                  return -EINVAL;

           len--;

           if ((s - path) + len > PATH_MAX)

                  return -ENAMETOOLONG;

           /* reverse fillup of target string from target to base */

           sd = target_sd;

           while (sd->s_parent && sd != base) {

                  int slen = strlen(sd->s_name);

                  len -= slen;

                  strncpy(s + len, sd->s_name, slen);

                  if (len)

                         s[--len] = '/';

                  sd = sd->s_parent;

           }

           return 0;

    }

    这段代码的逻辑比较简单.它先是找到目标路径和当前路径相同的父结点,然后再沿着目标结点往相同的父 

    结点向上走,将路径依次从缓存区后面往前面保存.

    例如: /sys/eric/kernel/test链接到了/sys/sys/device.

    它先找到两个路径共有的父结点/sys

    此时缓存区为:/sys

    然后,沿着/sys/sys/device/sys移动.路径加从缓存区的后面往前面加.依次为:

    1: /sys/     /device

    2:/sys/sys/device

    这样就找到了目的地的路径. ^_^.

    后面sysfs_put_link()的操作就不再讲述了,它只是释放掉缓存区.

    :小结

    在本小节里,我们深入探讨了sysfs文件系统的实现机理.这对于我们理解linux设备模型是很有帮助的

  • 相关阅读:
    每周总结8
    每周总结7
    每周总结6
    每周总结5
    每周总结4
    每周总结3
    每周总结2
    每周总结1
    Vue实例: 点击循环列表里的某行,改变该行的样式。默认第一行
    vue进阶面试题
  • 原文地址:https://www.cnblogs.com/Ph-one/p/5034926.html
Copyright © 2011-2022 走看看