文件系統的進化史和人類文明的進化有些相似,都是從低級、封閉乃至對抗走向高級、開放和包容。在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、公共的函數接口。