最近在做磁盤性能優化,需要結合文件系統原理去思考優化方向,因此借此機會進一步加深了對文件系統的認識。在看這篇文章之前,建議先看下前面一篇關於磁盤工作原理的解讀。下面簡單總結一些要點分享出來:
一、文件系統層次分析
由上而下主要分為用戶層、VFS層、文件系統層、緩存層、塊設備層、磁盤驅動層、磁盤物理層
用戶層:最上面用戶層就是我們日常使用的各種程序,需要的接口主要是文件的創建、刪除、打開、關閉、寫、讀等。
VFS層:我們知道Linux分為用戶態和內核態,用戶態請求硬件資源需要調用System Call通過內核態去實現。用戶的這些文件相關操作都有對應的System Call函數接口,接口調用 VFS對應的函數。
文件系統層:不同的文件系統實現了VFS的這些函數,通過指針注冊到VFS里面。所以,用戶的操作通過VFS轉到各種文件系統。文件系統把文件讀寫命令轉化為對磁盤LBA的操作,起了一個翻譯和磁盤管理的作用。
緩存層:文件系統底下有緩存,Page Cache,加速性能。對磁盤LBA的讀寫數據緩存到這里。
塊設備層:塊設備接口Block Device是用來訪問磁盤LBA的層級,讀寫命令組合之后插入到命令隊列,磁盤的驅動從隊列讀命令執行。Linux設計了電梯算法等對很多LBA的讀寫進行優化排序,盡量把連續地址放在一起。
磁盤驅動層:磁盤的驅動程序把對LBA的讀寫命令轉化為各自的協議,比如變成ATA命令,SCSI命令,或者是自己硬件可以識別的自定義命令,發送給磁盤控制器。Host Based SSD甚至在塊設備層和磁盤驅動層實現了FTL,變成對Flash芯片的操作。
磁盤物理層:讀寫物理數據到磁盤介質。
二、文件系統結構與工作原理(主要以ext4為例)
我們都知道,windows文件系統主要有fat、ntfs等,而linux文件系統則種類多的很,主要有VFS做了一個軟件抽象層,向上提供文件操作接口,向下提供標准接口供不同文件系統對接,下面主要就以EXT4文件系統為例,講解下文件系統結構與工作原理:
上面兩個圖大體呈現了ext4文件系統的結構,從中也相信能夠初步的領悟到文件系統讀寫的邏輯過程。下面對上圖里邊的構成元素做個簡單的講解:
引導塊:為磁盤分區的第一個塊,記錄文件系統分區的一些信息,,引導加載當前分區的程序和數據被保存在這個塊中。一般占用2kB,
超級塊:
超級塊用於存儲文件系統全局的配置參數(譬如:塊大小,總的塊數和inode數)和動態信息(譬如:當前空閑塊數和inode數),其處於文件系統開始位置的1k處,所占大小為1k。為了系統的健壯性,最初每個塊組都有超級塊和組描述符表(以下將用GDT)的一個拷貝,但是當文件系統很大時,這樣浪費了很多塊(尤其是GDT占用的塊多),后來采用了一種稀疏的方式來存儲這些拷貝,只有塊組號是3, 5 ,7的冪的塊組(譬如說1,3,5,7,9,25,49…)才備份這個拷貝。通常情況下,只有主拷貝(第0塊塊組)的超級塊信息被文件系統使用,其它拷貝只有在主拷貝被破壞的情況下才使用。
塊組描述符:
GDT用於存儲塊組描述符,其占用一個或者多個數據塊,具體取決於文件系統的大小。它主要包含塊位圖,inode位圖和inode表位置,當前空閑塊數,inode數以及使用的目錄數(用於平衡各個塊組目錄數),具體定義可以參見ext3_fs.h文件中struct ext3_group_desc。每個塊組都對應這樣一個描述符,目前該結構占用32個字節,因此對於塊大小為4k的文件系統來說,每個塊可以存儲128個塊組描述符。由於GDT對於定位文件系統的元數據非常重要,因此和超級塊一樣,也對其進行了備份。GDT在每個塊組(如果有備份)中內容都是一樣的,其所占塊數也是相同的。從上面的介紹可以看出塊組中的元數據譬如塊位圖,inode位圖,inode表其位置不是固定的,當然默認情況下,文件系統在創建時其位置在每個塊組中都是一樣的,如圖2所示(假設按照稀疏方式存儲,且n不是3,5,7的冪)
塊組:
每個塊組包含一個塊位圖塊,一個 inode 位圖塊,一個或多個塊用於描述 inode 表和用於存儲文件數據的數據塊,除此之外,還有可能包含超級塊和所有塊組描述符表(取決於塊組號和文件系統創建時使用的參數)。下面將對這些元數據作一些簡要介紹。
塊位圖:
塊位圖用於描述該塊組所管理的塊的分配狀態。如果某個塊對應的位未置位,那么代表該塊未分配,可以用於存儲數據;否則,代表該塊已經用於存儲數據或者該塊不能夠使用(譬如該塊物理上不存在)。由於塊位圖僅占一個塊,因此這也就決定了塊組的大小。
Inode位圖:
Inode位圖用於描述該塊組所管理的inode的分配狀態。我們知道inode是用於描述文件的元數據,每個inode對應文件系統中唯一的一個號,如果inode位圖中相應位置位,那么代表該inode已經分配出去;否則可以使用。由於其僅占用一個塊,因此這也限制了一個塊組中所能夠使用的最大inode數量。
Inode表:
Inode表用於存儲inode信息。它占用一個或多個塊(為了有效的利用空間,多個inode存儲在一個塊中),其大小取決於文件系統創建時的參數,由於inode位圖的限制,決定了其最大所占用的空間。
以上這幾個構成元素所處的磁盤塊成為文件系統的元數據塊,剩余的部分則用來存儲真正的文件內容,稱為數據塊,而數據塊其實也包含數據和目錄。
了解了文件系統的結構后,接下來我們來看看操作系統是如何讀取一個文件的:
大體過程如下:
1、根據文件所在目錄的inode信息,找到目錄文件對應數據塊
2、根據文件名從數據塊中找到對應的inode節點信息
3、從文件inode節點信息中找到文件內容所在數據塊塊號
4、讀取數據塊內容
到這里,相信很多人會有一個疑問,我們知道一個文件只有一個Inode節點來存放它的屬性信息,那么你可能會想如果一個大文件,那它的block一定是多個的,且可能不連續的,那么inode怎么來表示呢,下面的圖告訴你答案:
也就是說,如果文件內容太大,對應數據塊數量過多,inode節點本身提供的存儲空間不夠,會使用其他的間接數據塊來存儲數據塊位置信息,最多可以有三級尋址結構。
到這里,應該都已經非常清楚文件讀取的過程了,那么下面再拋出兩個疑問:
1、文件的拷貝、剪切的底層過程是怎樣的?
2、軟連接和硬連接分別是如何實現的?
下面來結合stat命令動手操作一下,便知真相:
1)拷貝文件:創建一個新的inode節點,並且拷貝數據塊內容
2)剪切文件:同個分區里邊mv,inode節點不變,只是更新目錄文件對應數據塊里邊的文件名和inode對應關系;跨分區mv,則跟拷貝一個道理,需要創建新的inode,因為inode節點不同分區是不能共享的。
3)軟連接:創建軟連接會創建一個新的inode節點,其對應數據塊內容存儲所鏈接的文件名信息,這樣原文件即便刪除了,重新建立一個同名的文件,軟連接依然能夠生效。
4)硬鏈接:創建硬鏈接,並不會新建inode節點,只是links加1,還有再目錄文件對應數據塊上增加一條文件名和inode對應關系記錄;只有將硬鏈接和原文件都刪除之后,文件才會真正刪除,即links為0才真正刪除。
三、文件順序讀寫和隨機讀寫
從前面文章了解了磁盤工作原理之后,也已經明白了為什么文件隨機讀寫速度會比順序讀寫差很多,這個問題在windows里邊更加明顯,為什么呢?究其原因主要與文件系統工作機制有關,fat和ntfs文件系統設計上,每個文件所處的位置相對連續甚至緊靠在一起,這樣沒有為每個文件留下足夠的擴展空間,因此容易產生磁盤碎片,用過windows系統的應該也知道,windows磁盤分區特別提供了磁盤碎片整理的高級功能。如下圖:
那回過來,看看linux 文件系統ext4,都說linux不需要考慮磁盤碎片,究竟是怎么回事?
主要是因為Linux的文件系統會將文件分散在整個磁盤,在文件之間留有大量的自由空間,而不是像Windows那樣將文件一個接一個的放置。當一個文件被編輯了並且變大了,一般都會有足夠的自由空間來保存文件。如果碎片真的產生了,文件系統就會嘗試在日常使用中將文件移動來減少碎片,所以不需要專門的碎片整理程序。但是,如果磁盤空間占用已經快滿了,那碎片是不可避免的,文件系統的設計本來就是用來滿足正常情況下使用的。如果磁盤空間不夠,那要么就是數據冗余了,要么就該換容量更大的磁盤。你可以使用fsck命令來檢測一下一個Linux文件系統的碎片化程度,只需要在輸出中查看非連續i節點個數(non-contiguous inodes)就可以了。
關於文件系統的就講這么多,下篇會講解linux內核提供的一個資源管控機制cgroup,分析其原理及使用過程。
ext4文件系統bug:
http://www.phoronix.com/scan.php?page=news_item&px=MTIxNDQ
ext4文件系統描述:
http://blog.csdn.net/liangchen0322/article/details/50365685
http://blog.csdn.net/fybon/article/details/26243971
hexdump查看磁盤結構信息:
http://www.cnblogs.com/jiangcsu/p/5737659.html