Linux 文件系統剖析

按照分層結構討論 Linux 文件系統


M. Tim Jones ([email protected]), 顧問工程師, Emulex Corp.


在文件系統方面,Linux® 可以算得上操作系統中的 “瑞士軍刀”。Linux 支持許多種文件系統,從日誌型文件系統到集羣文件系統和加密文件系統。對於使用標準的和比較奇特的文件系統以及開發文件系統來說,Linux 是極好的平臺。本文討論 Linux 內核中的虛擬文件系統(VFS,有時候稱爲虛擬文件系統交換器),然後介紹將文件系統連接在一起的主要結構。



基本的文件系統體系結構

Linux 文件系統體系結構是一個對複雜系統進行抽象化的有趣例子。通過使用一組通用的 API 函數,Linux 可以在許多種存儲設備上支持許多種文件系統。例如,read 函數調用可以從指定的文件描述符讀取一定數量的字節。read 函數不瞭解文件系統的類型,比如 ext3 或 NFS。它也不瞭解文件系統所在的存儲媒體,比如 AT Attachment Packet Interface(ATAPI)磁盤、Serial-Attached SCSI(SAS)磁盤或 Serial Advanced Technology Attachment(SATA)磁盤。但是,當通過調用 read 函數讀取一個文件時,數據會正常返回。本文講解這個機制的實現方法並介紹 Linux 文件系統層的主要結構。





回頁首


什麼是文件系統?

首先回答最常見的問題,“什麼是文件系統”。文件系統是對一個存儲設備上的數據和元數據進行組織的機制。由於定義如此寬泛,支持它的代碼會很有意思。正如前面提到的,有許多種文件系統和媒體。由於存在這麼多類型,可以預料到 Linux 文件系統接口實現爲分層的體系結構,從而將用戶接口層、文件系統實現和操作存儲設備的驅動程序分隔開。

文件系統作爲協議
另一種看待文件系統的方式是把它看作一個協議。網絡協議(比如 IP)規定了互聯網上傳輸的數據流的意義,同樣,文件系統會給出特定存儲媒體上數據的意義。

掛裝

在 Linux 中將一個文件系統與一個存儲設備關聯起來的過程稱爲掛裝(mount)。使用 mount 命令將一個文件系統附着到當前文件系統層次結構中(根)。在執行掛裝時,要提供文件系統類型、文件系統和一個掛裝點。

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


清單 1. 創建一個經過初始化的文件
                
$ dd if=/dev/zero of=file.img bs=1k count=10000
10000+0 records in
10000+0 records out
$

現在有了一個 10MB 的 file.img 文件。使用 losetup 命令將一個循環設備與這個文件關聯起來,讓它看起來像一個塊設備,而不是文件系統中的常規文件:

$ losetup /dev/loop0 file.img
$

這個文件現在作爲一個塊設備出現(由 /dev/loop0 表示)。然後用 mke2fs 在這個設備上創建一個文件系統。這個命令創建一個指定大小的新的 ext2 文件系統,見清單 2。


清單 2. 用循環設備創建 ext2 文件系統
                
$ mke2fs -c /dev/loop0 10000
mke2fs 1.35 (28-Feb-2004)
max_blocks 1024000, rsv_groups = 1250, rsv_gdb = 39
Filesystem label=
OS type: Linux
Block size=1024 (log=0)
Fragment size=1024 (log=0)
2512 inodes, 10000 blocks
500 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/point1
lost+found
$

如清單 4 所示,還可以繼續這個過程:在剛纔掛裝的文件系統中創建一個新文件,將它與一個循環設備關聯起來,再在上面創建另一個文件系統。


清單 4. 在循環文件系統中創建一個新的循環文件系統
                
$ dd if=/dev/zero of=/mnt/point1/file.img bs=1k count=1000
1000+0 records in
1000+0 records out
$ losetup /dev/loop1 /mnt/point1/file.img
$ mke2fs -c /dev/loop1 1000
mke2fs 1.35 (28-Feb-2004)
max_blocks 1024000, rsv_groups = 125, rsv_gdb = 3
Filesystem label=
...
$ mkdir /mnt/point2
$ mount -t ext2 /dev/loop1 /mnt/point2
$ ls /mnt/point2
lost+found
$ ls /mnt/point1
file.img lost+found
$

通過這個簡單的演示很容易體會到 Linux 文件系統(和循環設備)是多麼強大。可以按照相同的方法在文件上用循環設備創建加密的文件系統。可以在需要時使用循環設備臨時掛裝文件,這有助於保護數據。





回頁首


文件系統體系結構

既然已經看到了文件系統的構造方法,現在就看看 Linux 文件系統層的體系結構。本文從兩個角度考察 Linux 文件系統。首先採用高層體系結構的角度。然後進行深層次討論,介紹實現文件系統層的主要結構。





回頁首


高層體系結構

儘管大多數文件系統代碼在內核中(後面討論的用戶空間文件系統除外),但是圖 1 所示的體系結構顯示了用戶空間和內核中與文件系統相關的主要組件之間的關係。


圖 1. Linux 文件系統組件的體系結構
圖 1. Linux 文件系統組件的體系結構

用戶空間包含一些應用程序(例如,文件系統的使用者)和 GNU C 庫(glibc),它們爲文件系統調用(打開、讀取、寫和關閉)提供用戶接口。系統調用接口的作用就像是交換器,它將系統調用從用戶空間發送到內核空間中的適當端點。

VFS 是底層文件系統的主要接口。這個組件導出一組接口,然後將它們抽象到各個文件系統,各個文件系統的行爲可能差異很大。有兩個針對文件系統對象的緩存(inode 和 dentry)。它們緩存最近使用過的文件系統對象。

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

什麼是塊設備?
塊設備就是以塊(比如磁盤扇區)爲單位收發數據的設備,它們支持緩衝和隨機訪問(不必順序讀取塊,而是可以在任何時候訪問任何塊)等特性。塊設備包括硬盤、CD-ROM 和 RAM 盤。與塊設備相對的是字符設備,字符設備沒有可以進行物理尋址的媒體。字符設備包括串行端口和磁帶設備,只能逐字符地讀取這些設備中的數據。

這就是 VFS 和文件系統組件的高層情況。現在,討論實現這個子系統的主要結構。

主要結構

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. 向內核註冊的文件系統
圖 2. 向內核註冊的文件系統

VFS 中維護的另一個結構是掛裝的文件系統(見圖 3)。這個結構提供當前掛裝的文件系統(見 linux/include/linux/fs.h)。它鏈接下面討論的超級塊結構。


圖 3. 掛裝的文件系統列表
圖 3. 掛裝的文件系統列表

超級塊

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


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

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

inode 和 dentry

inode 表示文件系統中的一個對象,它具有惟一標識符。各個文件系統提供將文件名映射爲惟一 inode 標識符和 inode 引用的方法。圖 5 顯示 inode 結構的一部分以及兩個相關結構。請特別注意 inode_operationsfile_operations。這些結構表示可以在這個 inode 上執行的操作。inode_operations 定義直接在 inode 上執行的操作,而 file_operations 定義與文件和目錄相關的方法(標準系統調用)。


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

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

緩衝區緩存

除了各個文件系統實現(可以在 ./linux/fs 中找到)之外,文件系統層的底部是緩衝區緩存。這個組件跟蹤來自文件系統實現和物理設備(通過設備驅動程序)的讀寫請求。爲了提高效率,Linux 對請求進行緩存,避免將所有請求發送到物理設備。緩存中緩存最近使用的緩衝區(頁面),這些緩衝區可以快速提供給各個文件系統。





回頁首


有趣的文件系統

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

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





回頁首


結束語

分享這篇文章……

digg 將本文提交到 Digg
del.icio.us 發佈到 del.icio.us
Slashdot 提交到 Slashdot!

儘管文件系統的實現並不複雜,但它是可伸縮和可擴展的體系結構的好例子。文件系統體系結構已經發展了許多年,併成功地支持了許多不同類型的文件系統和許多目標存儲設備類型。由於使用了基於插件的體系結構和多層的函數間接性,Linux 文件系統在近期的發展很值得關注。



參考資料

學習

獲得產品和技術
  • Filesystem in Userspace(FUSE)是一個支持在用戶空間中開發文件系統的內核模塊。文件系統驅動程序實現將來自 VFS 的請求發送回用戶空間。這是一種在不借助內核開發的情況下開發文件系統的好方法。如果您精通 Python,也可以通過 LUFS-Python 用 Python 編寫文件系統。

  • 訂購 SEK for Linux,這有兩張 DVD,包含最新的 IBM Linux 試用版軟件,涉及 DB2®、Lotus®、Rational®、Tivoli® 和 WebSphere®。

  • 下載 IBM 產品評估版,試用來自 DB2®、Lotus®、Rational®、Tivoli® 和 WebSphere® 的應用程序開發工具和中間件產品。


討論


關於作者

M. Tim Jones

M. Tim Jones 是一名嵌入式軟件工程師,他是 GNU/Linux Application ProgrammingAI Application Programming 以及 BSD Sockets Programming from a Multilanguage Perspective 等書的作者。他的工程背景非常廣泛,從同步宇宙飛船的內核開發到嵌入式架構設計,再到網絡協議的開發。Tim 是位於科羅拉多州 Longmont 的 Emulex Corp. 的一名顧問工程師。


發佈了10 篇原創文章 · 獲贊 12 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章