在上一节中我们基本了解到了文件的一般属性,本节主要介绍VFS的结构:VFS 的结构主要包括了文件和文件系统
文件的表示方式:inode 是内核选择用于表示文件内容和相关元数据的方法,抽象对底层文件系统的访问:
1:inode 的操作,创建链接、文件重命名,在目录中生成新的文件,删除文件
2:文件操作,主要作用文件的数据内容。文件的读写操作和创建内存映射。
对应inode 的操作可以提供有关文件系统特性和现在的信息。打开的文件总是会分配到一个特定的进程中,所以内核必须在数据结构中存储文件和进程之间的关联,访问时使用文件描述符作为索引。
文件系统和超级块的信息:
超级块结构:存储文件系统相关的信息,对于磁盘文件系统来说,这个对象通常对应磁盘上的一个文件系统控制块,包括了相关文件系统中所有修改过的inode。
inode 数据结构中包含了各个文件操作的属性,比如:i_size 表示文件长度;i_block表示文件按块计算长度,每个VFS inode都有一个唯一的标识保存在i_ino里面,i_count 是个记录进程访问该结构的数目,i_nlink 则是表示硬件链接数。
inode 表示设备文件时需要i_rdev,它表示与那个设备进程通信
inode 表示块设备,i_pipe包含了用于实现管道的inode 的信息
inode 的操作:内核提供大量的函数对inode进行操作,主要有两个指针(i_op和i_fop)指向实现了上述抽象的函数。
i_op与特定的inode操作有关,i_fop提供了文件操作
inode 可以存储在链表中,因此inode 的三种状态:
inode 存在内存中,无关联到任何文件,无action 状态
inode 存在内存中 正在由一个文件或者多个进程使用,通常为一个文件,文件内容和inode 信息和块设备上的信息一致,文件已经挂载。
inode 结构存在内存中,有一个或者多个进程使用,其数据内容和存储介质不一致,此时为脏数据
特定于进程的信息:文件描述符用于一个进程内唯一标识地打开文件。内核能够在用户进程中的描述符和内核内部使用的结构之间建立一种关联。进程的文件数据都保存在fs中,其能打开的文件数由NR_OPEN_DEFAULT定义
文件操作主要包含以下几点:
file_operatuons 包含了指向所有可能文件操作的函数指针。该指针结构主要包含以下一些操作:
1:read和write 分别负责读写操作;其参数涉及有文件描述符,缓冲区和偏移量。另外一个参数是需要读写的数目量。
2:文件的内容映射到进程的虚拟地址空间中,通过mmap可以轻松访问文件。
3:ioctl 用于与硬件设备通信,所以只能用于设备文件。
VFS 命名空间是所有已经装载、构成某个容器目录树的文件系统的集合,同时nsproxy 该成员负责命名空间的处理。
目录项缓存:linux 使用目录项缓存(dentry)来快速访问此前的查找操作的结果,dentry不仅使得易于处理文件系统,对提高系统性能也很关键,通过最小话与底层文件系统的通信,加速了VFS的处理。dentry对象在内存中的组织主要涉及两个部分:
1:一个散列表包含了所有的dentry对象
2:一个LRU 链表,其中不再使用的对象将授予一个最后宽限期,
内核有几个标准函数用于辅助dentry,简化对dentry对象的操作,其实现主要是链表管理和数据结构的处理的练习。
上面描述的各种数据结构 是VFS 层的工作基础,现在接着处理VFS 对象(文件系统操作),我们从标准库用来与内核通信的系统调用说起。内核在编译的时候可以设置是否支持某种文件系统,因此一个文件系统的所用需要提前注册到内核。
1:注册文件系统 register_filesystem 用来注册文件系统
2:装载和卸载 需要向链表添加对象,由mount系统调用发起,每个装载的文件系统都对应于一个vfsmount结构的实例
3:超级块的管理 加载新文件系统时 vfsmount 并不是唯一需要在内核中创建的结构,而是需要开始于超级块的读取。在超级块的数据结构中有个s_root用于检测文件系统是否已经挂载,如果为NULL则该文件是个伪文件系统。
delete_inode 将inode从内存和底层介质删除,会移除指向相关数据块的指针,但文件数据不受影响。 当某个inode不再使用,vfs内部调用clear_inode 它释放了包含数据的所有相关内存页面
mount 系统调用的过程如下:
sys_mount->复制装载选项->do_mount->path_lookup->根据标志调用相应的装载函数。
do_new_mount(do_kernel_mount和do_add_mount)
do_kernel_mount 的任务是使用get_fs_type找到匹配的file实例,该函数会扫描链表找到是否已注册文件系统的链表返回正确的值,如果没有找到就自动加载对应的模块。
do_add_mount 处理一些必要的锁操作。
伪文件系统:
文件系统未必都需要底层块设备的支持,ramfs和tmpfs 是以内存做为存储的;procfs和sysfs 不需要后备储存,伪文件系统是不能装载的文件系统,因此不能直接再用户层看到。主要包括有(负责表示块设备的inode的bdev、处理管道的pipefs、处理套接字的socket)
MS_NOUSER 标识是mount 允许文件挂载的一个标识。
文件操作:
1:查找inode(nameidata) 通过name和flags,内核使用path_lookup函数查找路径或者文件名。
2:打开文件
3:读取和写入
read 三个参数:文件描述符、保存数据的缓存区、读取字符数目的长度
1:异步读取,如果设置了O_DIRECT标志则直接读取,不使用页缓存。
2:从映射中读取文件 通过映射机制,将文件中需要读取的部分映射到内存页中。通过大循环持续向内存写入,直到所有文件都传输到内存中。
下一节我们将重点介绍下EXT2和EXT3相关的内容,敬请关注!!