app开发者平台在数字化时代的重要性与发展趋势解析
972
2022-09-21
[深入理解文件系统之四] VFS和vnode
文件系统的进化史和人类文明的进化有些类似,都是从低级、封闭乃至对抗走向高级、开放和包容。在VFS一统江湖之前,其实还有它的前身FSS(File system Switch)。当时,人们寄希望于它能够兼容各种不同的文件系统。FSS中重要的数据结构mount/,它和传统indoe、文件系统类型的关系如下图:
SVR3最早是基于“Vnodes: An architecture for Multiple File-system Types in Sun Unix”这篇论文,这篇论文提出了实现统一文件系统的四个要求:
1.文件系统依赖层和文件系统非依赖层清晰第分开;
2.支持本地文件系统和NFS/RFS
3.支持NFS 服务器端
4.跨接口的文件系统操作应该是原子的
当然它的实现很复杂。其中一个主要的实现,就是从内核中去掉和具体文件系统相关的全局变量,以保证接口是可重入的。 例如,之前user结构中的u_base/u_count就需要去掉。
新设计的主要架构如下:
那么,vnode和我们现在通常所说的vfs又有何关系呢?关联在代码里又是如何实现的?这就需要了解vnode和vfs的数据结构和各自对应的操作。
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的统一抽象,它屏蔽底层具体文件系统的差异,向文件系统相关的系统调用层提供了统一、公共的函数接口。
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~