(本文剖析的内核代码以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,其定义如下linux 枚举文件,通过该插口变量,我们发觉其没有实现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的目录创建以及文件操作插口相关的系统调用等内容
sysfs目录相关的文件操作插口定义及说明(实现与文件描述符关联)
在之前剖析VFS时,我们晓得文件及目录的操作插口为
structfile_operations,该插口实现了open、read、write、readdir、llseek等插口的调用,这种插口实现了文件及目录的读取与写操作(关于file_operations结构体与文件描述符以及进程描述符的关系,请参考之前剖析的文章《LINUXVFS剖析之进程描述符与文件系统相关参数的关联》)。
针对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插口linux系统入门学习,我们主要关注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 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.若父目录支持kobjectnamespace,则调用该kobject的ktype->namespace插口,获取namespace变量以及kobject_ns_type。
(在sysfs中,针对netdevice,为保证在同一个目录下支持多个相同名称的netdevice,sysfs文件系统中引入了namespace的概念,作为sysfs目录/文件的tag。在创建目录及文件并将sysfs_dirent类型的变量插入到黑红树节点时,在判定是否存在相同的文件及目录时,也会依照kobject->name与sysfs_dirent->s_ns进行判别(sysfs目前仅支持nettype一种namespace类型,关于sysfsnamespace的详尽内容,可在Documentation/sysfs-tagging.txt中有详尽说明))
3.调用sysfs_new_dirent插口创建sysfs_dirent类型的变量linux删除命令,并设置其相应的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节点,那怎么显示目录呢?
我们在之前的剖析文档《LINUXVFS剖析之do_sys_open插口剖析与总结》中,在lookup_open插口中,若文件/目录的dentry没有创建linux 枚举文件,则创建该文件/目录的dentry,同时调用dir->i_op->lookup插口进行inode节点的创建(对sysfs而言,即调用sysfs_lookup进行inode节点的创建),这就实现了inode、dentry的创建。当在应用层通过ls等命令获取某一个sysfs目录的所有子节点时,则会调用sysfs_readdir,将所有该目录的子节点信息返回给应用层插口,之后我们即可见到该目录所有的子目录或子文件。
conststructfile_operationssysfs_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查看目录下的所有文件及子目录等内容(下一节剖析sysfs文件相关的创建以及文件操作插口等内容)。