天天看點

Linux 檔案系統Linux 檔案系統剖析

Linux 檔案系統剖析

按照分層結構讨論 Linux 檔案系統

簡介: 在檔案系統方面,Linux® 可以算得上作業系統中的 “瑞士軍刀”。Linux 支援許多種檔案系統,從日志型檔案系統到叢集檔案系統和加密檔案系統。對于使用标準的和比較奇特的檔案系統以及開發檔案系統來說,Linux 是極好的平台。本文讨論 Linux 核心中的虛拟檔案系統(VFS,有時候稱為虛拟檔案系統交換器),然後介紹将檔案系統連接配接在一起的主要結構。

本文的标簽:  filesystem, kernel, linux, ying,檔案系統, 檔案系統剖析

基本的檔案系統體系結構

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 檔案系統元件的體系結構

Linux 檔案系統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. 向核心注冊的檔案系統

Linux 檔案系統Linux 檔案系統剖析

VFS 中維護的另一個結構是挂裝的檔案系統(見圖 3)。這個結構提供目前挂裝的檔案系統(見 linux/include/linux/fs.h)。它連結下面讨論的超級塊結構。

圖 3. 挂裝的檔案系統清單

Linux 檔案系統Linux 檔案系統剖析

超級塊

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

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

Linux 檔案系統Linux 檔案系統剖析

超級塊中的一個重要元素是超級塊操作的定義。這個結構定義一組用來管理這個檔案系統中的 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 結構和相關聯的操作

Linux 檔案系統Linux 檔案系統剖析

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 進行開發是一種不錯的方法。

繼續閱讀