什么是文件系統?根據早期的 Linux 貢獻者和作家 Robert Love 所說,“文件系統是一個遵循特定結構的數據的分層存儲。” 不過,這種描述也同樣適用于 VFAT(虛擬文件分配表Virtual File Allocation Table)、Git 和Cassandra(一種 NoSQL 數據庫)。那么如何區別文件系統呢?
文件系統基礎概念
Linux 內核要求文件系統必須是實體,它還必須在持久對象上實現 open()、read() 和 write() 方法,并且這些實體需要有與之關聯的名字。從 面向對象編程 的角度來看,內核將通用文件系統視為一個抽象接口,這三大函數是“虛擬”的,沒有默認定義。因此,內核的默認文件系統實現被稱為虛擬文件系統(VFS)。
如果我們能夠 open()、read() 和 write(),它就是一個文件,如這個主控臺會話所示。
VFS 是著名的類 Unix 系統中 “一切皆文件” 概念的基礎。讓我們看一下它有多奇怪,上面的小小演示體現了字符設備 /dev/console 實際的工作。該圖顯示了一個在虛擬電傳打字控制臺(tty)上的交互式 Bash 會話。將一個字符串發送到虛擬控制臺設備會使其顯示在虛擬屏幕上。而 VFS 甚至還有其它更奇怪的屬性。例如,它可以在其中尋址。
我們熟悉的文件系統如 ext4、NFS 和 /proc 都在名為 file_operations 的 C 語言數據結構中提供了三大函數的定義。此外,個別的文件系統會以熟悉的面向對象的方式擴展和覆蓋了 VFS 功能。正如 Robert Love 指出的那樣,VFS 的抽象使 Linux 用戶可以輕松地將文件復制到(或復制自)外部操作系統或抽象實體(如管道),而無需擔心其內部數據格式。在用戶空間這一側,通過系統調用,進程可以使用文件系統方法之一 read() 從文件復制到內核的數據結構中,然后使用另一種文件系統的方法 write() 輸出數據。
屬于 VFS 基本類型的函數定義本身可以在內核源代碼的 fs/*.c 文件 中找到,而 fs/ 的子目錄中包含了特定的文件系統。內核還包含了類似文件系統的實體,例如 cgroup、/dev 和 tmpfs,在引導過程的早期需要它們,因此定義在內核的 init/ 子目錄中。請注意,cgroup、/dev 和 tmpfs 不會調用 file_operations 的三大函數,而是直接讀取和寫入內存。
下圖大致說明了用戶空間如何訪問通常掛載在 Linux 系統上的各種類型文件系統。像管道、dmesg 和 POSIX 時鐘這樣的結構在此圖中未顯示,它們也實現了 struct file_operations,而且其訪問也要通過 VFS 層。
How userspace accesses various types of filesystems
VFS 是個“墊片層”,位于系統調用和特定 file_operations 的實現(如 ext4 和 procfs)之間。然后,file_operations 函數可以與特定于設備的驅動程序或內存訪問器進行通信。tmpfs、devtmpfs 和 cgroup 不使用 file_operations 而是直接訪問內存。
VFS 的存在促進了代碼重用,因為與文件系統相關的基本方法不需要由每種文件系統類型重新實現。代碼重用是一種被廣泛接受的軟件工程最佳實踐!唉,但是如果重用的代碼引入了嚴重的錯誤,那么繼承常用方法的所有實現都會受到影響。
/tmp:一個小提示
找出系統中存在的 VFS 的簡單方法是鍵入 mount | grep -v sd | grep -v :/,在大多數計算機上,它將列出所有未駐留在磁盤上,同時也不是 NFS 的已掛載文件系統。其中一個列出的 VFS 掛載肯定是 /tmp,對吧?
誰都知道把 /tmp 放在物理存儲設備上簡直是瘋了!圖片:https://tinyurl.com/ybomxyfo
為什么把 /tmp 留在存儲設備上是不可取的?因為 /tmp 中的文件是臨時的(!),并且存儲設備比內存慢,所以創建了 tmpfs 這種文件系統。此外,比起內存,物理設備頻繁寫入更容易磨損。最后,/tmp 中的文件可能包含敏感信息,因此在每次重新啟動時讓它們消失是一項功能。
不幸的是,默認情況下,某些 Linux 發行版的安裝腳本仍會在存儲設備上創建 /tmp。如果你的系統出現這種情況,請不要絕望。按照一直優秀的 Arch Wiki 上的簡單說明來解決問題就行,記住分配給 tmpfs 的內存就不能用于其他目的了。換句話說,包含了大文件的龐大的 tmpfs 可能會讓系統耗盡內存并崩潰。
另一個提示:編輯 /etc/fstab 文件時,請務必以換行符結束,否則系統將無法啟動。(猜猜我怎么知道。)
/proc 和 /sys