存儲系列之 VFS虛擬文件系統簡介


引言:文件系統發展到一定階段,開始進一步抽象和分層。
 
前面我們介紹了ext系列文件系統和xfs文件系統,這些是Linux使用最多的文件系統,也是很多發布版本默認選擇的文件系統。而事實上,Linux支持的文件系統非常廣泛,Minix,FAT,VFAT,NFS,NTFS…等等。前面我們還介紹過一個分區代表了一個文件系統,不同的分區可以安裝不同的文件系統。那么在Linux系統中,如何管理和調用這些文件系統的接口呢?通過VFS來實現。

一、VFS定義和作用

VFS,Virtual File System虛擬文件系統,也稱為虛擬文件系統開關(Virtual Filesystem Switch),就是采用標准的Linux系統調用讀寫位於不同物理介質上的不同文件系統,即為各類文件系統提供了一個統一的操作界面和應用編程接口,VFS是一個內核軟件層
VFS是一個可以讓open()、read()、write()等系統調用不用關心底層的存儲介質和文件系統類型就可以工作的抽象層,如下圖所示。
 
我們知道在Linux系統中一切皆文件,在Linux系統中基本上把其中的所有內容都看作文件,除了我們普通意義理解的文件之外,目錄、字符設備、塊設備、 套接字、進程、線程、管道等都被視為是一個“文件”。例如對於塊設備,我們通過fdisk -l顯示塊設備列表,其實塊設備可以理解為在文件夾/dev下面的文件。只不過這些文件是特殊的文件。
VFS是一個抽象層,其向上提供了統一的文件訪問接口,而向下則兼容了各種不不同類型的文件系統。不僅僅是諸如Ext2、Ext4、XFS、windows家族的NTFS和Btrfs等常規意義上的文件系統,還可以是比如上圖的proc等偽文件系統和設備也可以是諸如NFS、CIFS等網絡文件系統
另外,VFS實現了一部分公共的功能,例如頁緩存和inode緩存等,從而避免多個文件系統重復實現的問題。有關高速緩存,后續章節還有介紹。  

二、VFS內部結構和對象類型  

VFS層通過定義一個清晰的VFS接口,以將文件系統的通用操作和具體實現分開。多個VFS接口的實現可以共存在同一台機器上,它允許訪問已裝在本地的多個類型的文件系統。

VFS提供了在網絡上唯一標識一個文件的機制。VFS基於稱為vnode文件表示結構,該結構包括一個數值標識符以表示位於整個網絡范圍內的唯一文件。該網絡范圍的唯一性用來支持網絡文件系統。內核中為每個活動節點(文件或目錄)保存一個vnode結構。

VFS根據文件系統類型調用特定文件類型操作以處理本地請求,通過調用NFS協議程序來處理遠程請求。文件句柄可以從相應的vnode中構造,並作為參數傳遞給程序。它的下一層實現文件系統類型或遠程文件系統協議。

下面簡要的討論一下Linux中的VFS結構。Linux VFS定義的4種主要對象類型是:
     超級塊對象(superblock object)表示整個文件系統。

    索引節點對象(inode object)表示一個單獨的文件。

    文件對象(file object)表示一個打開的文件。

    目錄項對象(dentry object)表示一個單獨的目錄項(或者稱作目錄條目)。

VFS對每種類型的對象都定義了一組必須實現的操作。這些類型的每一個對象都包含了一個指向函數表的指針。函數表列出了實際上實現特定對象的操作函數。
所有超級塊對象都以雙向循環鏈表的形式鏈接在一起,對象的自旋鎖(sb_lock)保護鏈表免受多處理器系統上的同時訪問。
 
一個進程對某個文件的操作在VFS結構中處理流程如下圖所示。

圖 3. VFS 對象

三、從VFS到具體文件系統

1、掛載

我們從上面得知,VFS可以管理各種文件系統,那么VFS和文件系統怎么關聯的呢?給用戶如何展示的呢?通過掛載。

如下圖所示,該系統根文件系統是Ext3文件系統,而在其/mnt目錄下面又分別掛載了Ext4文件系統和XFS文件系統。最后形成了一個由多個文件系統組成的文件系統樹。

掛載是用戶態發起的命令,就是我們知道的mount命令,該命令執行的時候需要指定文件系統的類型(本文假設Ext2)和文件系統數據的位置(也就是設備)。通過這些關鍵信息,VFS就可以完成Ext2文件系統的初始化,並將其關聯到當前已經存在的文件系統中,也就是建立其圖2所示的文件系統樹

在掛載的過程中,最為重要的數據結構是vfsmount,它代表一個掛載點。其次是dentry和inode,這兩個都是對文件的表示,且都會緩存在哈希表中以提高查找的效率。

其中inode是對磁盤上文件的唯一表示,其中包含文件的元數據(管理數據)和文件數據等內容,但不含文件名稱。而dentry則是為了Linux內核中查找文件方便虛擬出來的一個數據結構,其中包含文件名稱、子目錄(如果存在的話)和關聯的inode等信息。

dentry結構體最為關鍵,其維護了內核中的文件目錄樹。其中里面比較重要的幾個結構體分別是d_name、d_hash和d_subdirs。其中d_name代表一個路徑節點的名稱(文件夾名稱)、d_hash則用於構建哈希表,d_subdirs則是下級目錄(或文件)的列表。這樣,通過dentry就可以形成一個非常復雜的目錄樹。

2、文件處理流程

文件處理流程包括兩步:我們在訪問一個文件之前首先要打開它(open)文件訪問,然后進行文件的讀寫操作(read或者write)。

我們知道,在用戶態打開一個文件是返回的是一個文件描述符,其實也就是一個整數值;同時,訪問文件也是通過這個文件描述符進行的。那么操作系統是怎么通過這個整數值實現不同類型文件系統的訪問呢?不同文件系統的差異其實就是inode中初始化的函數指針的差異

在Linux操作系統中,文件的打開必須要與進程(或者線程)關聯,也就是說一個打開的文件必須隸屬於某個進程。

在linux內核當中一個進程通過task_struct結構體描述,而打開的文件則用file結構體描述,打開文件的過程也就是對file結構體的初始化的過程。在打開文件的過程中會將inode部分關鍵信息填充到file中,特別是文件操作的函數指針。在task_struct中保存着一個file類型的數組,而用戶態的文件描述符其實就是數組的下標。這樣通過文件描述符就可以很容易到找到file,然后通過其中的函數指針訪問數據。

我們以Ext2文件系統的寫數據為例來看看文件處理流程和各個層級之間的關系,如下圖。

在調用用戶態的寫數據接口的時候,需要傳入文件描述符。內核根據文件描述符找到file,然后調用函數接口(file->f_op->write)文件磁盤數據。其中file結構體的f_op指針就是在打開文件的時候通過inode初始化的。

 

 

參考資料:

《深入理解LINUX內核》第三版。

https://baijiahao.baidu.com/s?id=1621555464151870974&wfr=spider&for=pc

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM