3 sysfs文件系统

sysfs是一个基于内存的文件系统,它的作用是将内核信息以文件的方式提供给用户程序使用。该文件系统的目录层次结构严格按照内核的数据结构组织。除了二进制文件外(只有特殊场合才使用),sysfs文件内容均以ASCII格式保存,且一个文件只保存一个数据,另外,一个文件不可大于一个内存页(通常为4096字节)。

sysfs提供一种机制,使得可以显式的描述内核对象、对象属性及对象间关系。sysfs有两组接口,一组针对内核,用于将设备映射到文件系统中,另一组针对用户程序,用于读取或操作这些设备。表2描述了内核中的sysfs要素及其在用户空间的表现:

sysfs在内核中的组成要素

在用户空间的显示

内核对象(kobject)

目录

对象属性(attribute)

文件

对象关系(relationship)

链接(Symbolic Link)

表2:sysfs内部结构与外部表现

在Ubuntu或Fedora等Linux系统中,我们可以用ls –F <路径>命令来通过文件后缀查看文件类型。“/”表示文件夹,“@”表示链接,没有后缀就是文件了。

sysfs目录结构

/sys 下的子目录

所包含的内容

/sys/devices

这是内核对系统中所有设备的分层次表达模型,也是/sys文件系统管理设备的最重要的目录结构;

/sys/dev

这个目录下维护一个按字符设备和块设备的主次号码(major:minor)链接到真实的设备(/sys/devices下)的符号链接文件;

/sys/bus

这是内核设备按总线类型分层放置的目录结构, devices 中的所有设备都是连接于某种总线之下,在这里的每一种具体总线之下可以找到每一个具体设备的符号链接,它也是构成 Linux 统一设备模型的一部分;

/sys/class

这是按照设备功能分类的设备模型,如系统所有输入设备都会出现在/sys/class/input 之下,而不论它们是以何种总线连接到系统。它也是构成 Linux 统一设备模型的一部分;

/sys/kernel

这里是内核所有可调整参数的位置,目前只有 uevent_helper, kexec_loaded, mm, 和新式的 slab 分配器等几项较新的设计在使用它,其它内核可调整参数仍然位于sysctl(/proc/sys/kernel) 接口中;

/sys/module

这里有系统中所有模块的信息,不论这些模块是以内联(inlined)方式编译到内核映像文件(vmlinuz)中还是编译为外部模块(ko文件),都可能会出现在/sys/module 中:

  • 编译为外部模块(ko文件)在加载后会出现对应的/sys/module/<module_name>/,并且在这个目录下会出现一些属性文件和属性目录来表示此外部模块的一些信息,如版本号、加载状态、所提供的驱动程序等;
  • 编译为内联方式的模块则只在当它有非0属性的模块参数时会出现对应的/sys/module/<module_name>,这些模块的可用参数会出现在/sys/modules/<modname>/parameters/<param_name> 中,
    • 如/sys/module/printk/parameters/time这个可读写参数控制着内联模块printk在打印内核消息时是否加上时间前缀;
    • 所有内联模块的参数也可以由"<module_name>.<param_name>=<value>"的形式写在内核启动参数上,如启动内核时加上参数"printk.time=1"与向"/sys/module/printk/parameters/time"写入1的效果相同;
  • 没有非0属性参数的内联模块不会出现于此。

/sys/power

这里是系统中电源选项,这个目录下有几个属性文件可以用于控制整个机器的电源状态,如可以向其中写入控制命令让机器关机、重启等。

表3:sysfs目录结构

深入理解sysfs

sysfs_dirent是组成sysfs单元的基本数据结构,它是sysfs文件夹或文件在内存中的代表。sysfs_dirent只表示文件类型(文件夹/普通文件/二进制文件/链接文件)及层级关系,其它信息都保存在对应的inode中。我们创建或删除一个sysfs文件或文件夹事实上只是对以sysfs_dirent为节点的树的节点的添加或删除。sysfs_dirent数据结构如下:

struct sysfs_dirent {

atomic_t                s_count;

atomic_t                s_active;

struct sysfs_dirent  *s_parent;  /* 指向父节点 */

struct sysfs_dirent  *s_sibling;  /* 指向兄弟节点,兄弟节点是按照inode索引s_ino的大小顺序链接在一起的。*/

const char             *s_name;    /* 节点名称 */

union {

struct sysfs_elem_dir            s_dir;      /* 文件夹,s_dir->kobj指向sysfs对象 */

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;     /* inode索引,创建节点时被动态申请,通过此值和sysfs_dirent地址可以到inode散列表中获取inode结构 */

umode_t                s_mode;

struct iattr             *s_iattr;

};

inode(index node)中保存了设备的主从设备号、一组文件操作函数和一组inode操作函数。文件操作比较常见:open、read、write等。inode操作在sysfs文件系统中只针对文件夹实现了两个函数一个是目录下查找inode函数(.lookup=sysfs_lookup),该函数在找不到inode时会创建一个,并用sysfs_init_inode为其赋值;另一个是设置inode属性函数(.setattr=sysfs_setattr),该函数用于修改用户的权限等。inode结构如下:

struct inode {

struct hlist_node     i_hash;    /* 散列表链节 */

struct list_head       i_list;

struct list_head       i_sb_list;

struct list_head       i_dentry;  /* dentry链节 */

unsigned long         i_ino;   /* inode索引 */

atomic_t             i_count;

unsigned int           i_nlink;

uid_t                     i_uid;

gid_t                     i_gid;

dev_t                    i_rdev; /* 主从设备号 */

const struct inode_operations *i_op; /* 一组inode操作函数,可用其中lookup查找目录下的inode,对应sysfs为sysfs_lookup函数 */

const struct file_operations  *i_fop;    /* 一组文件操作函数,对于sysfs为sysfs的open/read/write等函数 */

struct super_block  *i_sb;

struct list_head       i_devices;

union {

struct pipe_inode_info    *i_pipe;

struct block_device       *i_bdev;

struct cdev            *i_cdev;

};

};

dentry(directory entry)的中文名称是目录项,是Linux文件系统中某个索引节点(inode)的链接。这个索引节点可以是文件,也可以是目录。引入dentry的目的是加快文件的访问。dentry数据结构如下:

struct dentry {

atomic_t d_count;         /* 目录项对象使用的计数器 */

unsigned int d_flags;             /* 目录项标志 */

spinlock_t d_lock;              /* 目录项自旋锁 */

int d_mounted;                     /* 对于安装点而言,表示被安装文件系统根项 */

struct inode *d_inode;           /* 文件索引节点(inode) */

/*

* The next three fields are touched by __d_lookup.  Place them here

* so they all fit in a cache line.

*/

struct hlist_node d_hash;       /* lookup hash list */

struct dentry *d_parent; /* parent directory */

struct qstr d_name;              /* 文件名 */

/*

* d_child and d_rcu can share memory

*/

union {

struct list_head d_child; /* child of parent list */

struct rcu_head d_rcu;

} d_u;

void *d_fsdata;                    /* 与文件系统相关的数据,在sysfs中指向sysfs_dirent */

unsigned char d_iname[DNAME_INLINE_LEN_MIN];      /* 存放短文件名 */

};

sysfs_dirent、inode、dentry三者关系:

图3-1:sysfs在内存中的形态

如上图sysfs超级块sysfs_sb、dentry根目录root、sysfs_direct根目录sysfs_root都是在sysfs初始化时创建。

sysfs_root下的子节点是添加设备对象或对象属性时调用sysfs_create_dir/ sysfs_create_file创建的,同时会申请对应的inode的索引号s_ino。注意此时并未创建inode。

inode是在用到的时候调用sysfs_get_inode函数创建并依据sysfs_sb地址和申请到的s_ino索引计算散列表位置放入其中。

dentry的子节点也是需要用的时候才会创建。比如open文件时,会调用path_walk根据路径一层层的查找指定dentry,如果找不到,则创建一个,并调用父dentry的inode的lookup函数(sysfs文件系统的为sysfs_lookup)查找对应的子inode填充指定的dentry。

这里有必要介绍一下sysfs_lookup的实现,以保证我们更加清晰地了解这个过程,函数主体如下:

static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)

{

struct dentry *ret = NULL;

struct sysfs_dirent *parent_sd = dentry->d_parent->d_fsdata; //获取父sysfs_direct

struct sysfs_dirent *sd;

struct inode *inode;

mutex_lock(&sysfs_mutex);

/* 在父sysfs_direct查找名为dentry->d_name.name的节点 */

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

/* no such entry */

if (!sd) {

ret = ERR_PTR(-ENOENT);

goto out_unlock;

}

/* 这儿就是通过sysfs_direct获取对应的inode,sysfs_get_inode实现原理上面已经介绍过了 */

/* attach dentry and inode */

inode = sysfs_get_inode(sd);

if (!inode) {

ret = ERR_PTR(-ENOMEM);

goto out_unlock;

}

/* 填充目录项,至此一个目录项创建完毕 */

/* instantiate and hash dentry */

dentry->d_op = &sysfs_dentry_ops;   /* 填充目录项的操作方法,该方法只提供一释放inode函数sysfs_d_iput */

dentry->d_fsdata = sysfs_get(sd);        //填充sysfs_direct

d_instantiate(dentry, inode);                 //填充inode

d_rehash(dentry);                               //将dentry加入hash表

out_unlock:

mutex_unlock(&sysfs_mutex);

return ret;

}

sysfs文件open流程

open的主要过程是通过指定的路径找到对应的dentry,并从中获取inode,然后获取一个空的file结构,将inode中相关内容赋值给file,这其中包括将inode的fop赋给file的fop。因此接下来调用的filp->fop->open其实就是inode里的fop->open。新的file结构对应一个文件句柄fd,这会作为整个open函数的返回值。之后的read/write操作就靠这个fd找到对应的file结构了。

图3-2是从网上找到的,清晰地描述了file和dentry以及inode之间的关系:

图3-2:file、dentry、inode关系

进程每打开一个文件,就会有一个file结构与之对应。同一个进程可以多次打开同一个文件而得到多个不同的file结构,file结构描述了被打开文件的属性,读写的偏移指针等等当前信息。

两个不同的file结构可以对应同一个dentry结构。进程多次打开同一个文件时,对应的只有一个dentry结构。dentry结构存储目录项和对应文件(inode)的信息。

在存储介质中,每个文件对应唯一的inode结点,但是,每个文件又可以有多个文件名。即可以通过不同的文件名访问同一个文件。这里多个文件名对应一个文件的关系在数据结构中表示就是dentry和inode的关系。

inode中不存储文件的名字,它只存储节点号;而dentry则保存有名字和与其对应的节点号,所以就可以通过不同的dentry访问同一个inode。

sysfs文件read/write流程

sysfs与普通文件系统的最大差异是,sysfs不会申请任何内存空间来保存文件的内容。事实上再不对文件操作时,文件是不存在的。只有用户读或写文件时,sysfs才会申请一页内存(只有一页),用于保存将要读取的文件信息。如果作读操作,sysfs就会调用文件的父对象(文件夹kobject)的属性处理函数kobject->ktype->sysfs_ops->show,然后通过show函数来调用包含该对象的外层设备(或驱动、总线等)的属性的show函数来获取硬件设备的对应属性值,然后将该值拷贝到用户空间的buff,这样就完成了读操作。写操作也类似,都要进行内核空间ßà用户空间内存的拷贝,以保护内核代码的安全运行。

图3-1为用户空间程序读sysfs文件的处理流程,其他操作类似:

图3-3:用户空间程序读sysfs文件的处理流程

转自:http://blog.csdn.net/s_e_a_n/article/details/5770693

sysfs文件系统的更多相关文章

  1. Linux设备模型——设备驱动模型和sysfs文件系统解读

    本文将对Linux系统中的sysfs进行简单的分析,要分析sysfs就必须分析内核的driver-model(驱动模型),两者是紧密联系的.在分析过程中,本文将以platform总线和spi主控制器的 ...

  2. sysfs文件系统的建立【转】

    http://blog.csdn.net/dndxhej/article/details/7434615 对sysfs和设备模型有了解的都会知道sysfs实际是为了将设备模型导出到用户空间的一个内存文 ...

  3. proc文件系统、sysfs文件系统、kobject操作

    Proc文件系统是提供一个接口给用户,让用户可以查看系统运行的一些状态信息,让用户修改内核的一些参数,比方说printk的打印级别就可以通过proc去修改 Sysfs文件系统, Sysfs is a ...

  4. sysfs文件系统学习--sysfs

    一.sysfs简介1.sysfs就是利用VFS的接口去读写kobject的层次结构,建立起来的文件系统.其更新与删除是那些xxx_register()/unregister()做的事 情.从sysfs ...

  5. [中英对照]The sysfs Filesystem | sysfs文件系统

    The sysfs Filesystem | sysfs文件系统 Abstract | 摘要 sysfs is a feature of the Linux 2.6 kernel that allow ...

  6. 概述sysfs文件系统【转】

    转自:http://blog.csdn.net/npy_lp/article/details/78933292 内核源码:linux-2.6.38.8.tar.bz2 目标平台:ARM体系结构 sys ...

  7. Sysfs文件系统与Linux设备模型

    转:http://www.360doc.com/content/11/1218/16/1299815_173168170.shtml sysfs把连接在系统上的设备和总线组织成为一个分级的目录及文件, ...

  8. Linux 内核sysfs 文件系统符号连接

    sysfs 文件系统有通常的树结构, 反映它代表的 kobjects 的层次组织. 但是内核中对象 间的关系常常比那个更加复杂. 例如, 一个 sysfs 子树 (/sys/devices )代表所有 ...

  9. linux sysfs文件系统

    个人理解:sysfs向用户空间展示了驱动设备的层次结构.我们都知道设备和对应的驱动都是由内核管理的,这些对于用户空间是不可见的.现在通过sysfs,可以在用户空间直观的了解设备驱动的层次结构. 我们来 ...

随机推荐

  1. ActiveMQ 性能调优

    本章重点 学习普通的性能调优技巧 怎么优化生产者和消费者 调优实例 简介 ActiveMQ 的性能高度依赖于多种不同的因素,包括:网络代理拓扑架构,传输层,底层网络的服务质量和速度,硬件,操作系统和 ...

  2. C++(十八) — 内存模式、堆和栈

    1.内存模式 一个程序执行时,先复制到内存,然后CPU逐句读取指令执行. 每个存储单元存放一个字节(8bit)数据,每个有一个唯一的地址,地址是顺序编码的.比如:一台计算机256MB内存,则有256* ...

  3. [转载]java开发实现word在线编辑及流转

    OA公文流转系统主要用于处理企业日常工作中内外部的各种公文,包括了公文的拟稿.审批.传阅.公告.归档,多层上级可以对下级撰写的公文进行逐级审批或修改,待最高级人员确认无误后即可进行核稿和发文等操作,最 ...

  4. 【hive】函数大全

    数学函数 Return Type Name (Signature) Description DOUBLE round(DOUBLE a) Returns the rounded BIGINT valu ...

  5. 使用Spring实现MySQL读写分离

    1. 为什么要进行读写分离 大量的JavaWeb应用做的是IO密集型任务, 数据库的压力较大, 需要分流 大量的应用场景, 是读多写少, 数据库读取的压力更大 一个很自然的思路是使用一主多从的数据库集 ...

  6. Qt5学习笔记——QRadioButton与QbuttonGroup

    [我是小标题:使用QToolButton实现radio button功能.] QRadioButton是什么?  下图是Windows系统中典型的radio button显示效果.    QRadio ...

  7. VGGNet网络结构

    深度神经网络一般由卷积部分和全连接部分构成.卷积部分一般包含卷积(可以有多个不同尺寸的核级联组成).池化.Dropout等,其中Dropout层必须放在池化之后.全连接部分一般最多包含2到3个全连接, ...

  8. js错误Cannot set property 'action' of null

    Cannot set property 'action' of null [自己解决问题答案] 应该放到form里面 [网上答案]是页面无法加载完毕执行代码.可以把获取元素等一系列的操作放在 wind ...

  9. Windows下Python安装lxml

    1.下载easy_install的安装包,下载地址:https://pypi.Python.org/pypi/setuptools 我是Windows7,所以直接下载Windows(Simplify) ...

  10. 剑指offer-第六章面试中的各项能力(扑克牌中的顺子)

    //扑克牌的顺子 //题目:在一个扑克牌中随机的抽5张牌,看是不是顺子.大小王为0,A为1,J为11,Q为12,K为13.其他数字为自己本身. //思路:大小王可以代表任意一个数字,因此我们在看是不是 ...