想弄清楚Linux文件系統的運行機制嗎?
Linux文件系統作為操作系統的核心組成部分,其運行機制也是我們程序員需要了解和掌握的,磁盤為系統提供了最基本的持久化存儲,文件系統則在磁盤的基礎上提供系統里所有文件的管理,在Linux里一切皆文件,不僅普通的文件和目錄,就連塊設備、套接字、管道等,也都要通過統一的文件系統來管理。今天我們就一起來聊一聊:磁盤和文件系統是怎么工作的?
索引節點和目錄項
在Linux文件系統中,一個文件的元數據包括:目錄項、索引節點、數據塊。
- 目錄項:簡稱為dentry,用來記錄文件的名字、索引節點指針以及與其他目錄項的關聯關系。多個關聯的目錄項,就構成了文件系統的目錄結構。目錄項是由內核維護的一個內存數據結構,所以通常也被叫做目錄項緩存(Cache)。
- 索引節點:簡稱為inode,用來記錄文件的元數據,包括inode 編號、文件大小、訪問權限、修改日期、數據的位置、鏈接數等,索引節點信息會持久化到磁盤中存儲,占用磁盤空間。
- 數據塊: 簡稱為block,存儲文件數據的地方。磁盤的最小存儲單位叫做扇區(Sector),每個扇區存儲512字節,相當于0.5KB,操作系統讀取硬盤的時候,不會一個扇區一個扇區地讀取,這樣效率太低,而是一次性連續讀取多個扇區,即一次性讀取一個"塊"(block)。這種由多個扇區組成的"塊",是文件存取的最小單位。"塊"的大小,最常見的是4KB(八個sector)。
索引節點是存儲在硬盤上的數據,那么為了加速文件的訪問,通常會把索引節點加載到內存中。另外,磁盤進行格式化的時候,會被分成三個存儲區域,分別是超級塊、索引節點區和數據塊區。
- 超級塊,用來存儲文件系統的詳細信息,比如塊個數、塊大小、空閑塊等等。
- 索引節點區,用來存儲索引節點。
- 數據塊區,用來存儲文件或目錄數據。
虛擬文件系統
Linux系統中的虛擬文件系統(VFS,Virtual File System)是一個抽象層,用于提供統一的文件系統接口,使得用戶和應用程序能夠以相同的方式訪問不同類型的文件系統,而無需關心底層文件系統的具體實現。
用戶程序和 glibc 庫都是屬于用戶空間的,本質都是用戶程序。應用層的程序和glibc通過調用“系統調用層(SCI)”的函數,完成對文件的操作。這些函數是 Linux 內核對外提供的函數接口,用戶通過這些函數向系統申請操作。比如系統cat命令,它首先調用open()函數 ,打開一個文件;然后調用read() 函數,讀取文件的內容;最后再調用 write()函數 ,把文件內容輸出到控制臺的標準輸出中。常見的文件系統類型又可以分為以下幾大類:
- 基于本地磁盤:EXT3、EXT4、XFS、OverlayFS 等。這類文件系統的特點是數據直接存儲在計算機本地掛載的磁盤中,性能好,沒有網絡IO的訪問消耗。
- 基于網絡文件系統:NFS、CIFS/SMB、CephFS、GlusterFS等,這類文件的特點是它們允許用戶通過網絡訪問和管理文件。分布式、跨平臺、靈活性和可擴縮性是它們的最大優勢。
- 基于內存文件系統:tmpfs、ramfs、/proc等,這些基于內存的文件系統通常用于特定的用途,如臨時文件存儲、緩存、快速數據訪問等。它們提供了在內存中進行文件讀寫操作的高性能解決方案,但也需要注意內存限制和數據易失性的特點。
文件 I/O
我們對磁盤進行的分區、格式化這些操作就是建立不同類型的文件系統,這些文件系統需要通過mount的方式掛載到Linux的VFS上的某個目錄才能被系統使用,應用程序對文件的讀寫有不同的方式,也就是我們常說的I/O類型,以下是我們常見的I/O類型。
緩沖與非緩沖I/O
- 所謂不帶緩沖,并不是指內核不提供緩沖,而是只單純的系統調用,不是函數庫的調用。系統內核對磁盤的讀寫都會提供一個塊緩沖,當用write函數對其寫數據時,直接調用系統調用,將數據寫入到塊緩沖進行排隊,當塊緩沖達到一定的量時,才會把數據寫入磁盤。因此所謂的不帶緩沖的I/O是指進程不提供緩沖功能。每調用一次write或read函數,直接系統調用。(內核提供緩沖的)。
- 而帶緩沖的I/O是指進程對輸入輸出流進行了改進,提供了一個流緩沖。當用write函數寫數據時,先把數據寫入流緩沖區中,當達到一定條件,比如流緩沖區滿了,這時候才會把數據一次送往內核提供的塊緩沖,再經塊緩沖寫入磁盤。(雙重緩沖)
- 因此,帶緩沖的I/O在往磁盤寫入相同的數據量時,會比不帶緩沖的I/O調用系統調用的次數要少。
直接 I/O與非直接I/O
- 直接I/O:就是應用程序直接訪問磁盤數據,而不經過內核緩沖區,這樣做的目的是減少一次從內核緩沖區到用戶程序緩存的數據復制。
- 非直接I/O:就是文件讀寫時,先要經過系統的頁緩存,然后再由內核或額外的系統調用后寫入磁盤。
- 對于直接I/O,如果訪問的數據不在應用程序緩存中,那么每次數據都會直接從磁盤加載,這種直接加載的效率會比較慢。但是類型于數據庫管理系統這類應用,它們更傾向于選擇它們自己的緩存機制,因為數據庫管理系統往往比操作系統更了解數據庫中存放的數據,使用直接I/O更合適。
阻塞I/O和非阻塞I/O
- 阻塞I/O:應用進程調用I/O操作時阻塞,只有等待要操作的數據準備好,并復制到應用進程的緩沖區中才返回。特點是:實現難度低、開發應用較容易,適用并發量小的網絡應用開發。
- 非阻塞I/O:是指應用程序執行 I/O 操作后,不會阻塞當前的線程,可以繼續執行其他的任務,隨后再通過輪詢或者事件通知的形式,獲取調用的結果。特點是:相對來說復雜一些。適用并發量較小、且不需要及時響應的網絡應用開發
同步和異步 I/O
- 同步I/O:是指應用程序執行 I/O 操作后,要一直等到整個 I/O 完成后,才能獲得 I/O 響應。
- 異步I/O:是指應用程序執行 I/O 操作后,不用等待完成和完成后的響應,而是繼續執行就可以。等到這次 I/O 完成后,響應會用事件通知的方式,告訴應用程序。
關于文件的一些常見小知識
磁盤剩余空間還很多,新建文件和目錄報空間不足。
- 排查思路:大概率是小文件太多,inode用完了,可以使用df -i。
du和df統計的硬盤使用情況不一致問題。
- du是統計被文件系統記錄到的每個文件的大小,然后進行累加得到的總大小,是通過文件系統獲取到的。而df主要是從超級塊(superblock)中讀入硬盤使用信息,df獲取到的是磁盤塊被使用的情況。產生這種情況大概率是有文件被刪除了,但是有別的進程正在使用它(占有句柄),可以通過lsof | grep deleted查到。當進程停止或者被kill時,這些空間將被釋放。
我們查詢磁盤容量的時候,Used+Avail的大小為啥總是小于總容量(SIze)。
- 為了預防緊急情況,linux ext文件系統會預留部分硬盤空間,具體預留的數值可以通過tune2fs -l [dev_name] | grep “Reserved block count”查看到,(dev_name)是設備名,這里預留的空間會被df計算到已用空間中,從而導致df和du統計不一致。如果需要調整預留空間大小, 我們可以使用tune2fs -m [size] [dev_name]來進行調整。