Chinaunix首页 | 论坛 | 博客
  • 博客访问: 99444
  • 博文数量: 47
  • 博客积分: 1032
  • 博客等级: 少尉
  • 技术积分: 759
  • 用 户 组: 普通用户
  • 注册时间: 2011-01-19 15:47
文章分类
文章存档

2012年(26)

2011年(21)

分类: LINUX

2012-07-03 17:34:16

一:前言

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

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

二:sysfs的初始化和挂载

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

int __init sysfs_init(void)

{

   int err =-ENOMEM;

//创建一个分配sysfs_dirent的cache

//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!\n");

                    err = PTR_ERR(sysfs_mount);

                    sysfs_mount = NULL;

                    unregister_filesystem(&sysfs_fs_type);

                    goto out_err;

              }

       } else

             goto out_err;

out:

       returnerr;

out_err:

      kmem_cache_destroy(sysfs_dir_cachep);

      sysfs_dir_cachep = NULL;

       goto out;

}

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

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

 

 

 

上图表示的是一个kobject的层次结构.dentry的d_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_type的get_sb

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

static int sysfs_get_sb(struct file_system_type*fs_type,

       intflags, const char *dev_name, void *data, struct vfsmount *mnt)

{

       returnget_sb_single(fs_type, flags, data, sysfs_fill_super, mnt);

}

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

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

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

{

       structinode *inode;

       structdentry *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;

 

       /* getroot inode, initialize and unlock it */

       inode =sysfs_get_inode(&sysfs_root);

       if(!inode) {

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

             return -ENOMEM;

       }

 

       /*instantiate and link root dentry */

       root =d_alloc_root(inode);

       if(!root) {

             pr_debug("%s: could not get root dentry!\n",__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)

{

       structinode *inode;

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

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

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

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

             sysfs_init_inode(sd, inode);

 

       returninode;

}

首先,它以sysfs文件系统的super_block和struct 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)

{

       structbin_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)) {

       caseSYSFS_DIR:

              inode->i_op =&sysfs_dir_inode_operations;

             inode->i_fop = &sysfs_dir_operations;

             inode->i_nlink = sysfs_count_nlink(sd);

             break;

       caseSYSFS_KOBJ_ATTR:

             inode->i_size = PAGE_SIZE;

              inode->i_fop =&sysfs_file_operations;

             break;

       caseSYSFS_KOBJ_BIN_ATTR:

             bin_attr = sd->s_bin_attr.bin_attr;

             inode->i_size = bin_attr->size;

             inode->i_fop = &bin_fops;

             break;

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

{

       structdentry *ret = NULL;

       //取得父结点对应的sysfs_dirent

       structsysfs_dirent *parent_sd = dentry->d_parent->d_fsdata;

       structsysfs_dirent *sd;

       structinode *inode;

 

      mutex_lock(&sysfs_mutex);

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

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

 

       /* nosuch 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;

       //关联dentry与sysfs_dirent

      dentry->d_fsdata = sysfs_get(sd);

      d_instantiate(dentry, inode);

      d_rehash(dentry);

 

 out_unlock:

      mutex_unlock(&sysfs_mutex);

       returnret;

}

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

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

将其关联到denry中.

sysfs_find_dirent()如下:

 

struct sysfs_dirent *sysfs_find_dirent(structsysfs_dirent *parent_sd,

                                   constunsigned char *name)

{

       structsysfs_dirent *sd;

 

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

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

                    return sd;

       returnNULL;

}

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

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

 

三:在sysfs文件系统中创建目录

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

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

int sysfs_create_dir(struct kobject * kobj)

{

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

       returnerror;

}

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

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

                   const char *name, struct sysfs_dirent **p_sd)

{

       //指定目录的模式

       umode_tmode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO;

       structsysfs_addrm_cxt acxt;

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

 

       /* linkin */

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

 

       returnrc;

}

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

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

 

四:在sysfs中创建一般属性文件

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

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

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

{

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

 

      //kobject->sd: 为kobject表示目录的structsysfs_dirent结构

       returnsysfs_add_file(kobj->sd, attr, SYSFS_KOBJ_ATTR);

 

}

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

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

                int type)

{

       //文件对应的属性

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

       structsysfs_addrm_cxt acxt;

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

{

       ……

       …….

       caseSYSFS_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, structfile *file)

{

       structsysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;

       structkobject *kobj = attr_sd->s_parent->s_dir.kobj;

       structsysfs_buffer *buffer;

       struct sysfs_ops *ops;

       int error= -EACCES;

 

       /* needattr_sd for attr and ops, its parent for kobj */

       if(!sysfs_get_active_two(attr_sd))

             return -ENODEV;

 

       /* everykobject 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\n", kobject_name(kobj));

             WARN_ON(1);

             goto err_out;

       }

 

       /* Fileneeds write support.

        * Theinode's perms must say it's ok,

        * and wemust have a store method.

        */

       if(file->f_mode & FMODE_WRITE) {

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

                    goto err_out;

       }

 

       /* Fileneeds read support.

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

        * mustbe a show method for it.

        */

       if(file->f_mode & FMODE_READ) {

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

                    goto err_out;

       }

 

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

        * it infile->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;

 

       /* makesure we have open dirent struct */

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

       error =sysfs_get_open_dirent(attr_sd, buffer);

       if(error)

             goto err_free;

 

       /* opensucceeded, put active references */

      sysfs_put_active_two(attr_sd);

       return 0;

 

 err_free:

      kfree(buffer);

 err_out:

      sysfs_put_active_two(attr_sd);

       returnerror;

}

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

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)

{

       structsysfs_buffer * buffer = file->private_data;

       ssize_tlen;

 

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

       returnlen;

}

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

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

Flush_wirte_buffer()代码如下:

static int

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

{

       structsysfs_dirent *attr_sd = dentry->d_fsdata;

       structkobject *kobj = attr_sd->s_parent->s_dir.kobj;

       structsysfs_ops * ops = buffer->ops;

       int rc;

 

       /* needattr_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);

 

       returnrc;

}

我们在分析open()操作的时候曾分析到.buffer的ops是kobject->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)

{

       structsysfs_buffer * buffer = file->private_data;

       ssize_tretval = 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\n",

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

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

                                    buffer->count);

out:

      mutex_unlock(&buffer->mutex);

       returnretval;

}

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

fill_read_buffer的代码如下:

static int fill_read_buffer(struct dentry * dentry,struct sysfs_buffer * buffer)

{

       struct sysfs_dirent*attr_sd = dentry->d_fsdata;

       structkobject *kobj = attr_sd->s_parent->s_dir.kobj;

       structsysfs_ops * ops = buffer->ops;

       int ret =0;

       ssize_tcount;

 

       if(!buffer->page)

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

       if(!buffer->page)

             return -ENOMEM;

 

       /* needattr_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);

 

       /*

        * Thecode 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\n",

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

       }

       returnret;

}

在这里,我们看到,最终会调用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);

 

       returnsysfs_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, conststruct 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;

       structsysfs_dirent  *s_parent;

       structsysfs_dirent  *s_sibling;

       constchar             *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;

       };

 

       unsignedint           s_flags;

      ino_t                     s_ino;

      umode_t                s_mode;

       structiattr             *s_iattr;

};

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

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

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

 

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

sysfs_dirent ->s_bin_attr.bin_attr->read和sysfs_dirent ->s_bin_attr.bin_attr->write

 

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

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

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

{

       structsysfs_dirent *parent_sd = NULL;

       structsysfs_dirent *target_sd = NULL;

       structsysfs_dirent *sd = NULL;

       structsysfs_addrm_cxt acxt;

       interror;

 

      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 fromit.

        */

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

       returnerror;

}

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

sd->s_symlink.target_sd = target_sd;

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

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

const struct inode_operationssysfs_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)

{

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

       }

       returnPTR_ERR(cookie);

}

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

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

 

Follow_link()的操作如下示:

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

{

       int error= -ENOMEM;

       unsignedlong page = get_zeroed_page(GFP_KERNEL);

       if (page)

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

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

       returnNULL;

}

Ne_set_link()是将page中的值copy到nd->saved_name[]中.

sysfs_getlink()的代码如下:

sysfs_getlink()-àsysfs_get_target_path()

static int sysfs_get_target_path(struct sysfs_dirent*parent_sd,

                             structsysfs_dirent *target_sd, char *path)

{

       structsysfs_dirent *base, *sd;

       char *s =path;

       int len =0;

 

       /* go upto 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;

       }

       /* checklimits */

       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设备模型是很有帮助的

阅读(852) | 评论(1) | 转发(1) |