首頁>技術>

Linux 檔案系統體系結構是一個對複雜系統進行抽象化的有趣例子。透過使用一組通用的 API 函式,Linux 可以在許多種儲存裝置上支援許多種檔案系統。例如,read 函式呼叫可以從指定的檔案描述符讀取一定數量的位元組。

read 函式不瞭解檔案系統的型別,比如 ext3 或 NFS。它也不瞭解檔案系統所在的儲存媒體,比如 AT Attachment Packet Interface(ATAPI)磁碟、Serial-Attached SCSI(SAS)磁碟或 Serial Advanced Technology Attachment(SATA)磁碟。

但是,當透過呼叫 read 函式讀取一個檔案時,資料會正常返回。本文講解這個機制的實現方法並介紹 Linux 檔案系統層的主要結構。

什麼是檔案系統?

首先回答最常見的問題,“什麼是檔案系統”。檔案系統是對一個儲存裝置上的資料和元資料進行組織的機制。由於定義如此寬泛,支援它的程式碼會很有意思。正如前面提到的,有許多種檔案系統和媒體。由於存在這麼多型別,可以預料到 Linux 檔案系統介面實現為分層的體系結構,從而將使用者介面層、檔案系統實現和操作儲存裝置的驅動程式分隔開。

掛裝

在 Linux 中將一個檔案系統與一個儲存裝置關聯起來的過程稱為掛裝(mount)。使用 mount 命令將一個檔案系統附著到當前檔案系統層次結構中(根)。在執行掛裝時,要提供檔案系統型別、檔案系統和一個掛裝點。

為了說明 Linux 檔案系統層的功能(以及掛裝的方法),我們在當前檔案系統的一個檔案中建立一個檔案系統。實現的方法是,首先用 dd 命令建立一個指定大小的檔案(使用 /dev/zero 作為源進行檔案複製)—— 換句話說,一個用零進行初始化的檔案,見清單 1。

清單 1. 建立一個經過初始化的檔案

$ dd if=/dev/zero of=file.img bs=1k count=1000010000+0 records in10000+0 records out$

現在有了一個 10MB 的 file.img 檔案。使用 losetup 命令將一個迴圈裝置與這個檔案關聯起來,讓它看起來像一個塊裝置,而不是檔案系統中的常規檔案:

$ losetup /dev/loop0 file.img$

這個檔案現在作為一個塊裝置出現(由 /dev/loop0 表示)。然後用 mke2fs 在這個裝置上建立一個檔案系統。這個命令建立一個指定大小的新的 ext2 檔案系統,見清單 2。

清單 2. 用迴圈裝置建立 ext2 檔案系統

$ mke2fs -c /dev/loop0 10000mke2fs 1.35 (28-Feb-2004)max_blocks 1024000, rsv_groups = 1250, rsv_gdb = 39Filesystem label=OS type: LinuxBlock size=1024 (log=0)Fragment size=1024 (log=0)2512 inodes, 10000 blocks500 blocks (5.00%) reserved for the super user...$

使用 mount 命令將迴圈裝置(/dev/loop0)所表示的 file.img 檔案掛裝到掛裝點 /mnt/point1。注意,檔案系統型別指定為 ext2。掛裝之後,就可以將這個掛裝點當作一個新的檔案系統,比如使用 ls 命令,見清單 3。

清單 3. 建立掛裝點並透過迴圈裝置掛裝檔案系統

$ mkdir /mnt/point1$ mount -t ext2 /dev/loop0 /mnt/point1$ ls /mnt/point1lost+found$

如清單 4 所示,還可以繼續這個過程:在剛才掛裝的檔案系統中建立一個新檔案,將它與一個迴圈裝置關聯起來,再在上面建立另一個檔案系統。

清單 4. 在迴圈檔案系統中建立一個新的迴圈檔案系統

$ dd if=/dev/zero of=/mnt/point1/file.img bs=1k count=10001000+0 records in1000+0 records out$ losetup /dev/loop1 /mnt/point1/file.img$ mke2fs -c /dev/loop1 1000mke2fs 1.35 (28-Feb-2004)max_blocks 1024000, rsv_groups = 125, rsv_gdb = 3Filesystem label=...$ mkdir /mnt/point2$ mount -t ext2 /dev/loop1 /mnt/point2$ ls /mnt/point2lost+found$ ls /mnt/point1file.img lost+found$

透過這個簡單的演示很容易體會到 Linux 檔案系統(和迴圈裝置)是多麼強大。可以按照相同的方法在檔案上用迴圈裝置建立加密的檔案系統。可以在需要時使用迴圈裝置臨時掛裝檔案,這有助於保護資料。

文章福利】需要C/C++ Linux伺服器架構師學習資料加群812855908(資料包括C/C++,Linux,golang技術,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒體,CDN,P2P,K8S,Docker,TCP/IP,協程,DPDK,ffmpeg等)

圖 1. Linux 檔案系統元件的體系結構

使用者空間包含一些應用程式(例如,檔案系統的使用者)和 GNU C 庫(glibc),它們為檔案系統呼叫(開啟、讀取、寫和關閉)提供使用者介面。系統呼叫介面的作用就像是交換器,它將系統呼叫從使用者空間傳送到核心空間中的適當端點。

VFS 是底層檔案系統的主要介面。這個元件匯出一組介面,然後將它們抽象到各個檔案系統,各個檔案系統的行為可能差異很大。有兩個針對檔案系統物件的快取(inode 和 dentry)。它們快取最近使用過的檔案系統物件。

每個檔案系統實現(比如 ext2、JFS 等等)匯出一組通用介面,供 VFS 使用。緩衝區快取會快取檔案系統和相關塊裝置之間的請求。例如,對底層裝置驅動程式的讀寫請求會透過緩衝區快取來傳遞。這就允許在其中快取請求,減少訪問物理裝置的次數,加快訪問速度。以最近使用(LRU)列表的形式管理緩衝區快取。注意,可以使用 sync 命令將緩衝區快取中的請求傳送到儲存媒體(迫使所有未寫的資料傳送到裝置驅動程式,進而傳送到儲存裝置)。

主要結構

Linux 以一組通用物件的角度看待所有檔案系統。這些物件是超級塊(superblock)、inode、dentry 和檔案。超級塊在每個檔案系統的根上,超級塊描述和維護檔案系統的狀態。檔案系統中管理的每個物件(檔案或目錄)在 Linux 中表示為一個 inode。inode 包含管理檔案系統中的物件所需的所有元資料(包括可以在物件上執行的操作)。

另一組結構稱為 dentry,它們用來實現名稱和 inode 之間的對映,有一個目錄快取用來儲存最近使用的 dentry。dentry 還維護目錄和檔案之間的關係,從而支援在檔案系統中移動。最後,VFS 檔案表示一個開啟的檔案(儲存開啟的檔案的狀態,比如寫偏移量等等)。

虛擬檔案系統層

VFS 作為檔案系統介面的根層。VFS 記錄當前支援的檔案系統以及當前掛裝的檔案系統。

可以使用一組註冊函式在 Linux 中動態地新增或刪除檔案系統。核心儲存當前支援的檔案系統的列表,可以透過 /proc 檔案系統在使用者空間中檢視這個列表。這個虛擬檔案還顯示當前與這些檔案系統相關聯的裝置。在 Linux 中新增新檔案系統的方法是呼叫 register_filesystem。這個函式的引數定義一個檔案系統結構(file_system_type)的引用,這個結構定義檔案系統的名稱、一組屬性和兩個超級塊函式。也可以登出檔案系統。

在註冊新的檔案系統時,會把這個檔案系統和它的相關資訊新增到 file_systems 列表中(見圖 2 和 linux/ include/ linux/ mount.h)。這個列表定義可以支援的檔案系統。在命令列上輸入 cat /proc/filesystems,就可以檢視這個列表。

圖 2. 向核心註冊的檔案系統

圖 3. 掛裝的檔案系統列表

超級塊

超級塊結構表示一個檔案系統。它包含管理檔案系統所需的資訊,包括檔案系統名稱(比如 ext2)、檔案系統的大小和狀態、塊裝置的引用和元資料資訊(比如空閒列表等等)。超級塊通常儲存在儲存媒體上,但是如果超級塊不存在,也可以實時建立它。可以在 ./linux/include/linux/fs.h 中找到超級塊結構(見圖 4)。

圖 4. 超級塊結構和 inode 操作

超級塊中的一個重要元素是超級塊操作的定義。這個結構定義一組用來管理這個檔案系統中的 inode 的函式。例如,可以用 alloc_inode 分配 inode,用 destroy_inode 刪除 inode。可以用 read_inode和 write_inode 讀寫 inode,用 sync_fs 執行檔案系統同步。可以在 ./linux /include/ linux/fs.h 中找到 super_operations 結構。每個檔案系統提供自己的 inode 方法,這些方法實現操作並向 VFS 層提供通用的抽象。

inode和dentry

inode 表示檔案系統中的一個物件,它具有惟一識別符號。各個檔案系統提供將檔名對映為惟一 inode 識別符號和 inode 引用的方法。

圖 5 顯示 inode 結構的一部分以及兩個相關結構。請特別注意 inode_operations 和file_operations。這些結構表示可以在這個 inode 上執行的操作。inode_operations 定義直接在 inode 上執行的操作,而 file_operations 定義與檔案和目錄相關的方法(標準系統呼叫)。

圖 5. inode 結構和相關聯的操作

inode 和目錄快取分別儲存最近使用的 inode 和 dentry。注意,對於 inode 快取中的每個 inode,在目錄快取中都有一個對應的 dentry。可以在 ./linux/include/linux/fs.h 中找到 inode 和dentry 結構。

緩衝區快取

除了各個檔案系統實現(可以在 ./linux/fs 中找到)之外,檔案系統層的底部是緩衝區快取。這個元件跟蹤來自檔案系統實現和物理裝置(透過裝置驅動程式)的讀寫請求。為了提高效率,Linux 對請求進行快取,避免將所有請求傳送到物理裝置。快取中快取最近使用的緩衝區(頁面),這些緩衝區可以快速提供給各個檔案系統。

有趣的檔案系統

本文沒有討論 Linux 中可用的具體檔案系統,但是值得在這裡稍微提一下。Linux 支援許多種檔案系統,包括 MINIX、MS-DOS 和 ext2 等老式檔案系統。Linux 還支援 ext3、JFS 和 ReiserFS 等新的日誌型檔案系統。另外,Linux 支援加密檔案系統(比如 CFS)和虛擬檔案系統(比如 /proc)。

最後一種值得注意的檔案系統是 Filesystem in Userspace(FUSE)。這種檔案系統可以將檔案系統請求透過 VFS 傳送回用戶空間。所以,如果您有興趣建立自己的檔案系統,那麼透過使用 FUSE 進行開發是一種不錯的方法。

結束語

儘管檔案系統的實現並不複雜,但它是可伸縮和可擴充套件的體系結構的好例子。檔案系統體系結構已經發展了許多年,併成功地支援了許多不同型別的檔案系統和許多目標儲存裝置型別。由於使用了基於外掛的體系結構和多層的函式間接性,Linux 檔案系統在近期的發展很值得關注。

10
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • MySQL重大新增功能