对linux有了整体了解之后,接下来就是针对各个子系统逐个深入研究摸索了。^-^
这次先从VFS入手,
其实很多子模块都和进程关联的,所以我们在分析子模块的时候可以从进程入手,从进程的什么地方开始呢?当时是数据结构task_struct。
首先看下task_struct结构里的files、fs和mount定义。fs_struct定义了和文件系统相关的数据结构,例如根目录、当前目录、所属用户以及执行权限等。
files_struct结构定义如下:
文件描述符表struct fdtable结构定义如下:
struct fdtable里定义了structfile结构:
struct file_operations结构信息:里面主要是和file相关的操作函数
用 struct inode 表示文件本身。
其中struct inode_operations结构定义了inode相关的操作函数: 根据上面的file_struct结构,我们先简单总结下结构对应关系图,如下所示:
接下来看下namespace结构定义,包含了6项,其中mnt_namepsace就是和文件系统相关的,即mount mnt_namespace的根目录挂载点对应的结构是struct mount:
总结下mnt_namespace结构,如图所示:
接下来看下fs_struct:
struct path结构:
fs_struct主要定义了根目录和当前目录的目录项
总结一下fs_struct和vfsmount以及dentry之前的关系图,如下所示:
最后从整体上对vfs做一个系统性的总结:
下面将以ls为例看下linux是如何根据文件名查找对应的inode的。
按照以往套路,通过GDB单步调试ls,之前已经编译了coreutils,所以这里执行gdb ls即可。
通过GDB发现,ls最终调用的是lstat,而lstat最终又是通过系统调用stat。参数主要是char * filename和struct stat stat。
stat系统调用在fs/stat.c文件里
user_path_at_empty函数最终调用filename_lookup函数, 其中nameidata是记录路径查找相关数据的结构:
last_type类型总共有5种,即enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND};其中LAST_NORM表示普通的路径名;LAST_ROOT是表示/;LAST_DOT 和LAST_DOTDOT 分别表示. 和..;LAST_BIND表示符号链接。
总结一下根据文件名查找inode的过程,主要有两方面查找过程,一是快速查找,二是慢查找;快查找主要是在cache中进行查找,而快查找失败了,则进行慢查找,慢查找是查到文件对应所属的文件系统,然后调用文件系统的look_up进行查找,而且会遍历磁盘,因此效率会慢。(看到这里的fast和slow,想起了网卡收包里内存的快慢分配) 在之前的文章df和du的区别一文中已经介绍了区别,当时说df统计的是文件系统所占用的存储空间,du统计的是文件占用存储空间的大小。其实文件系统对应的就是super_block,df通过调用statvfs,统计super_block中所有block的大小。
当然,在实际工作中可能会遇到一些比较奇怪的问题。例如某个挂载点实际只有100G的存储空间,但是du看到的对应目录下却超过了100G?有可能是某个inode是个硬链接,即某个文件有多个硬链接,du统计的是文件大小,遇到一个则算一个。
总结:本文主要从task_struct结构里的fs、files和mnt_namepsace入手,阐述vfs的数据结构以及super_block、file、inode、mount、dentry之间的关联图。并且从一个实际例子通过文件名查找inode的过程分析了内核的查找过程。