一、VFS定義和作用

二、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到具體文件系統
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