文件系统的进化史和人类文明的进化有些相似,都是从低级、封闭乃至对抗走向高级、开放和包容。在VFS一统江湖以前,其实还有它的前身FSS(File system Switch)。当时,人们寄但愿于它可以兼容各类不一样的文件系统。FSS中重要的数据结构mount/,它和传统indoe、文件系统类型的关系以下图:node
可是FSS是短命的,被后来SVR3里面引入的vnode快速代替。但咱们不能忘记FSS最重要的贡献: 把不一样的文件系统以mount的形式挂载起来。数组
SVR3最先是基于“Vnodes: An architecture for Multiple File-system Types in Sun Unix”这篇论文,这篇论文提出了实现统一文件系统的四个要求:服务器
1.文件系统依赖层和文件系统非依赖层清晰第分开;数据结构
2.支持本地文件系统和NFS/RFS架构
3.支持NFS 服务器端ide
4.跨接口的文件系统操做应该是原子的函数
固然它的实现很复杂。其中一个主要的实现,就是从内核中去掉和具体文件系统相关的全局变量,以保证接口是可重入的。 例如,以前user结构中的u_base/u_count就须要去掉。ui
新设计的主要架构以下:
this
那么,vnode和咱们如今一般所说的vfs又有何关系呢?关联在代码里又是如何实现的?这就须要了解vnode和vfs的数据结构和各自对应的操做。spa
vnode相关的操做针对vnode,包括vop_open/vop_close/vop_rdwr/vop_ioctl/vop_select/vop_getattr/vop_setattr/vop_access
vnode数据结构的成员包括:
v_flag: VROOT/VNOMAP/VNOSWAP/VNOMOUNT/VISSWAP
v_count: similar as i_count
v_shlockc: shared lock counter
v_exlockc: number of exclusive locks on the vnode
v_vfsmountedhere: points to the vfs structure of the mounted file-system,
v_op: vnode operations associated with this file type
v_vfsp: points to the vfs structure for this file system
v_type: specifies the type of file that the vnode represents VREG/VDIR/VBLK/VCHR/VLINK/VFIFO/VXNAM
v_data: used to reference private data such as a copy of the on-disk inode
能够看到,经过上面的v_vfsmountedhere和v_vfsp和虚拟文件系统关联了起来。
此外,实现对不一样文件系统的统一接口,就须要屏蔽用户态实现io请求的差别。一种避免直接处理或引用user数据结构中的IO相关的信息的方法是把这些信息打包,这就引入了uio_iov的数据结构。 这个数据结构包含下面的成员:
uio_iov:基于user地址和字节数的iovec结构的数组指针;
uio_iovcnt: number of iovec;
uio_offset: 文件内读和写起始的地方
uio_segflg:代表当前请求是来自用户态仍是内核态
uio_resid:后面未完成的IO个数
经过上面的uio数据结构,它实现了两个主要的好处:
1. user area access was implemented so that NFS can make a call to the underlying filesystem;
2. readv()/writev() can be implemented
而VFS像一个文件系统公共抽象层,或者说是父类也好。 每一个挂载的文件系统都对应一个vfs数据结构,该结构的主要成员包括vfs_ops。其中vfs_ops是每一个具体的文件系统须要实现的一组操做函数。这些操做包括:
vfs_mount()
vfs_unmount()
vfs_root():返回当前文件系统的根vnode节点,用在路径名解析
vfs_statfs()
vfs_sync()
vfs_fid(): NFS用它来为特殊的vnode构建一个文件句柄
vfs_vget(): NFS用它来把上面返回的文件句柄转换成一个vnode
根据上面的数据结构和操做能够看到:
1. vfs_ops相关的操做只是设计到文件系统层面,无论具体的vnode操做;
2. VFS主要是实现一个抽象类的功能、接口或机制,屏蔽了不一样文件的操做上的区别,让应用层看到全部的文件系统操做都是透明的。
讨论完了vfs,再回过头看vnode,它的主要操做以下:
vop_open():针对设备相关的文件操做,通常在vop_lookup返回vnode后调用
vop_close(): 针对设备相关的文件的操做
vop_rdwr():读或写文件,和具体IO相关的信息是经过uio数据结构传递进来的
vop_ioctl():借助针对文件的ioctl操做,函数可以传到设备驱动
vop_select():实现select()系统调用
vop_getattr(): stat()系统调用的实现用这个函数来填充vattr数据结构
vop_setattr():让调用者来设置文件大小、模式、User ID、group ID、 file times等属性
vop_access():让调用者来检查文件读、写、可执行等权限
vop_lookup():根据指定目录对应的vnode和要查找的文件/设备名, 返回指定目录下和设备名对应的vnode
vop_create():在参数vnode指定的目录下建立一个新文件, 文件属性是经过参数vattr传递进来的。
vop_remove()删除一个目录entry
vop_link()
vop_rename()
vop_mkdir()
vop_rmdir()
vop_readdir()
vop_symlik()
vop_readlink()
上面都是来实现各自对应的系统调用
vop_fsync(): 把内存中任何改动的数据刷到磁盘,实现fsync()系统调用
vop_inacive():当内核中文件系统无关层没有任何模块再使用vnode的时候,文件系统经过这个调用来释放这个vnode;
vop_bmap():向内核VM(Virtual memory subsystem)请求页,以便VM可以把逻辑文件的偏移映射到物理磁盘的偏移
vop_strategy():在vop_bmap()以后,VM或者buffer cache 层调用这个函数来读取文件块到内存中。
vop_bread():从指定的vnode对应的文件中读取逻辑块,而且从buffer cache中返回指向这个逻辑块的buffer.
vop_brelse():在vop_bread()调用以后,释放buffre.
能够看到vnode是对不一样文件系统中各类特殊inode的统一抽象,它屏蔽底层具体文件系统的差别,向文件系统相关的系统调用层提供了统1、公共的函数接口。