LINUX SYSFS文件系统分析之三 目录创建及相应系统调用操作分析

(本文分析的内核代码以3.10.0为主)

        以上两节分别介绍了sysfs文件系统的注册与挂载、以及sysfs相关的结构体定义及联系,本章主要是介绍sysfs文件系统相关的操作,包括文件及目录的创建等功能。

       在sysfs文件系统的注册过程中,有一点我们没有细讲,即当创建根inode和根dentry的时候,为根inode设置了两个操作接口指针,即i_op、i_fop(针对目录inode节点的i_op指针而言,其提供了文件及目录的创建等接口,如create、mkdir等,关于inode结构体的详细信息,请参考文章《LINUX 文件系统分析之VFS相关的概念及数据结构》)。而针对sysfs根inode的i_op,其定义如下,通过该接口变量,我们发现其没有实现create、mkdir等接口,因此我们可以断定,在应用层无法进行文件或者目录的创建,各位读者可以在sysfs目录下确认。

const struct inode_operations sysfs_dir_inode_operations = {

.lookup                = sysfs_lookup,

.permission        = sysfs_permission,

.setattr        = sysfs_setattr,

.getattr        = sysfs_getattr,

.setxattr        = sysfs_setxattr,

};

    

在上一篇文章中,我们已经介绍了相应的结构体变量,并且说明了其以sysfs_dirent结构体来

进行文件及目录的组织与关联操作。下面我们分为几个部分说明sysfs的文件操作,主要包括

  1. 目录相关的操作
  2. 文件相关的操作

 

sysfs目录相关的文件操作接口定义及说明(实现与文件描述符关联)

在之前分析VFS时,我们知道文件及目录的操作接口为

struct file_operations,该接口实现了open、read、write、readdir、llseek等接口的调用,这些接口实现了文件及目录的读取与写操作(关于 file_operations结构体与文件描述符以及进程描述符的关系,请参考之前分析的文章《LINUX VFS分析之进程描述符与文件系统相关参数的关联》)。

针对sysfs的目录操作接口,请定义如下:

const struct file_operations sysfs_dir_operations = {

.read                = generic_read_dir,

.readdir        = sysfs_readdir,

.release        = sysfs_dir_release,

.llseek                = sysfs_dir_llseek,

};

针对sysfs目录而言,其主要提供了readdir、llseek接口,我们主要关注readdir,因为提供了该接口以后,我们即可在应用层通过ls命令,查找sysfs文件下任何一个目录的所有孩子(包括子目录和子文件)。

 

sysfs_readdir分析

该接口的定义如下,其主要实现的功能有:

1.获取该目录parent_sd->s_dir.children.rb_node节点下所有符合要求的子节点(包括子目录与子文件),即子节点指向的namespace变量与该目录对应的namespace变量相同。

2.通过该接口我们知道,当应用层通过系统调用readdir查找某一个sysfs目录下的所有子节点时,其是根据该目录对应sysfs_dirent->s_dir.children.rb_node节点进行查找的,即根据sysfs_dirent变量所关联的红黑树结构进行查找,因此仅在存在sysfs_dirent变量且在某一个sysfs_dirent类型变量的s_dir.children.rb_node节点时,才表明该目录或文件存在。

static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir)

{

struct dentry *dentry = filp->f_path.dentry;

struct sysfs_dirent * parent_sd = dentry->d_fsdata;

struct sysfs_dirent *pos = filp->private_data;

enum kobj_ns_type type;

const void *ns;

ino_t ino;

loff_t off;



    /*获取父目录的namespace变量即类型,用于查找其namespace下的节点等*/

type = sysfs_ns_type(parent_sd);

ns = sysfs_info(dentry->d_sb)->ns[type];



    /*若pos为0,则将"."子目录fill至读取缓存中*/

if (filp->f_pos == 0) {

ino = parent_sd->s_ino;

if (filldir(dirent, ".", 1, filp->f_pos, ino, DT_DIR) == 0)

filp->f_pos++;

else

return 0;

}

   

    /*若pos为1,则将".."子目录fill至读取缓存中*/

if (filp->f_pos == 1) {

if (parent_sd->s_parent)

ino = parent_sd->s_parent->s_ino;

else

ino = parent_sd->s_ino;

if (filldir(dirent, "..", 2, filp->f_pos, ino, DT_DIR) == 0)

filp->f_pos++;

else

return 0;

}

mutex_lock(&sysfs_mutex);

off = filp->f_pos;

    /*调用sysfs_dir_pos,从父目录的parent_sd->s_dir.children.rb_node查找所有拥有指定namespace的子节点*/

for (pos = sysfs_dir_pos(ns, parent_sd, filp->f_pos, pos);

     pos;

     pos = sysfs_dir_next_pos(ns, parent_sd, filp->f_pos, pos)) {

const char * name;

unsigned int type;

int len, ret;



name = pos->s_name;

len = strlen(name);

ino = pos->s_ino;

type = dt_type(pos);

off = filp->f_pos = pos->s_hash;

filp->private_data = sysfs_get(pos);



mutex_unlock(&sysfs_mutex);

ret = filldir(dirent, name, len, off, ino, type);

mutex_lock(&sysfs_mutex);

if (ret < 0)

break;

}

mutex_unlock(&sysfs_mutex);



/* don't reference last entry if its refcount is dropped */

if (!pos) {

filp->private_data = NULL;



/* EOF and not changed as 0 or 1 in read/write path */

if (off == filp->f_pos && off > 1)

filp->f_pos = INT_MAX;

}

return 0;

}

SYSFS提供给kernel模块的目录创建接口

    针对sysfs的目录相关的操作接口与变量,均在文件dir.c中实现,针对目录操作,我们主要介绍目录创建这几个接口。

目录创建

     我们首先介绍目录创建接口,即sysfs_create_dir接口,该接口主要用于创建目录,其处理流程如下所示。

该接口主要完成的功能如下:

1.根据传递的kobject,获取该kobject的父目录对应的sysfs_dirent类型的变量:

    a. 若找到则记为parent_sd;

    b. 若没有找到,则以sysfs_root记为父目录对应的sysfs_dirent(即本次在sysfs文件系统的根目录

       下创建子目录)

2.若父目录支持kobject namespace,则调用该kobject的ktype->namespace接口,获取namespace变量以及kobject_ns_type。

(在sysfs中,针对net device,为保证在同一个目录下支持多个相同名称的net device,sysfs文件系统中引入了namespace的概念,作为sysfs 目录/文件的tag。在创建目录及文件并将sysfs_dirent类型的变量插入到红黑树节点时,在判断是否存在相同的文件及目录时,也会根据kobject->name与sysfs_dirent->s_ns进行判断(sysfs目前仅支持net type一种namespace类型,关于sysfs namespace的详细内容,可在Documentation/sysfs-tagging.txt中有详细说明))

3.调用sysfs_new_dirent接口创建sysfs_dirent类型的变量,并设置其相应的namespace相关的成员;

4.调用sysfs_add_one(sysfs_add_one->__sysfs_add_one->sysfs_link_sibling),将该sysfs_dirent类型的变量插入到其父目录sysfs_dirent的子目录的红黑树节点中。至此完成目录的创建。

 

注意,仅在上述第4步执行完成后,方可标识一个sysfs目录或文件的创建完成,因为在sysfs的readdir接口中,其根据父目录对应sysfs_dirent类型变量的s_dir.children.rb_node节点查找子节点,因此若一个sysfs_dirent变量未插入到一个父节点的s_dir.children.rb_node中,则尚未完成创建操作。

 

在该接口调用中,并没有创建目录相关的inode、dentry节点,那如何显示目录呢?

  1. 我们在之前的分析文档《LINUX VFS分析之do_sys_open接口分析与总结》中,在lookup_open接口中,若文件/目录的dentry没有创建,则创建该文件/目录的dentry,同时调用dir->i_op->lookup接口进行inode节点的创建(对sysfs而言,即调用sysfs_lookup进行inode节点的创建),这就实现了inode、dentry的创建。
  2. 当在应用层通过ls等命令获取某一个sysfs目录的所有子节点时,则会调用sysfs_readdir,将所有该目录的子节点信息返回给应用层接口,然后我们即可看到该目录所有的子目录或子文件。
const struct file_operations sysfs_dir_operations = {

.read                = generic_read_dir,

.readdir        = sysfs_readdir,

.release        = sysfs_dir_release,

.llseek                = sysfs_dir_llseek,

};

 sysfs提供了sysfs_create_dir接口后,主要供kobject相关的接口kobject_add_internal使用,该接口实现了根据kobject变量创建对应目录及其属性文件的功能,该接口的处理流程如下。

 

1.调用kobj_kset_join,将该kobject插入到其所指向的kset的kob子链表中

2.调用create_dir接口,创建该kobject对应的目录及属性文件。

 

 

 

kobject_add_internal接口主要由kobject_add_varg接口调用,而kobject_add_varg则由kobject_add、kobject_init_and_add,而关于kobject相关的接口,我们后续会单独介绍,此处暂时不再展开。

 

以上介绍了sysfs目录的创建以及目录相关的文件操作接口,主要属性sysfs目录相关的一些概念,以及为何我们可以在应用层通过ls查看目录下的所有文件及子目录等内容。

发布了140 篇原创文章 · 获赞 30 · 访问量 45万+

猜你喜欢

转载自blog.csdn.net/lickylin/article/details/102597246
今日推荐