1. 程式人生 > >儲存系統科普——檔案系統介紹

儲存系統科普——檔案系統介紹

簡介

該篇blog只是儲存系列科普文章中的第二篇,所有文章請參考:

部落格所有文章

在工程架構領域裡,儲存是一個非常重要的方向,這個方向從底至上,我分成了如下幾個層次來介紹:

  1. 硬體層:講解磁碟,SSD,SAS, NAS, RAID等硬體層的基本原理,以及其為作業系統提供的儲存介面;
  2. 作業系統層:即檔案系統,作業系統如何將各個硬體管理並對上提供更高層次介面;
  3. 單機引擎層:常見儲存系統對應單機引擎原理大概介紹,利用檔案系統介面提供更高級別的儲存系統介面;
  4. 分散式層:如何將多個單機引擎組合成一個分散式儲存系統;
  5. 查詢層:使用者典型的查詢語義表達以及解析;

檔案系統磁碟分配

在儲存知識棧上提到檔案系統, 大家首先就關心他是如何管理好硬體層提供的磁碟空間的。

本文開篇就先描述典型的幾種檔案系統管理磁碟的技術方案: 連續分配, 鏈式分配, 索引分配。

連續分配

顧名思義, 就是建立檔案的時候, 給分配一組連續的塊。在單獨拿出一塊地方儲存各個檔案的meta資訊, meta資訊也很簡單, 包含檔名, 起始位置和長度即可, 這個存放meta資訊的地方也叫做FAT(文件分配表)。

如下圖:

連續分配示意圖

該方案的優點:

  1. 簡便, 適用於一次性寫入操作;
  2. 所需磁碟尋道次數和尋道時間最少;

缺點也很明顯:

  1. 檔案不能動態增長, 因為後面的塊可能已經分配給別人了;
  2. 不利於檔案的插入和刪除, 插入檔案之前需要宣告檔案大小;
  3. 外部碎片問題;

為了克服連續分配的問題, 又進化出來了鏈式分配方案。

鏈式分配

鏈式分配即將檔案塊像連結串列一樣管理起來, 每個塊中放一個指標, 指標指向下一個檔案塊所在的位置。這樣在FAT中儲存也很簡單, 只需要儲存檔名, 起始塊號和結束塊號(都可以不存)。

如下圖:

鏈式分配示意圖

該方案的優點:

  1. 提高磁碟利用率, 沒有碎片問題
  2. 有利於檔案的插入, 刪除

缺點:

  1. 存取速度慢, 需要移動的磁軌次數多, 尋道時間長;
  2. 讀寫任何一個地方都需要沿著指標前進直到找到對應塊為止;
  3. 連結佔據了一定的磁碟空間, 資料不是嚴格按照塊大小分配;

索引分配

這是一個折衷方案, 也是現在大部分檔案系統採用的方案, 他綜合了連續分配和鏈式分配的好處。

該方案會在FAT中儲存所有檔案塊的位置, 各檔案系統都有一套自己對應的細節分配策略, 會保證一個檔案儘量連續的同時, 又避免出現大量的磁碟碎片。

如下圖:

索引分配示意圖

該方案的優點是:

  1. 能夠保持好大部分檔案的區域性性
  2. 滿足檔案插入, 刪除的高效
  3. 隨機讀寫不需要沿著指標前進

缺點:

  1. 會有較多的磁碟尋道次數
  2. 索引表本身管理複雜, 也會帶來額外的系統開銷

ext檔案系統

基本概念

  1. 塊. 即Block, 資料儲存的最小單元, 每個塊都有一個唯一的地址, 從0開始, 起源於第一個可以用來儲存資料的扇區;
  2. 超級塊. 超級塊儲存了各種檔案系統的meta資訊, 比如塊大小資訊。他位於檔案系統的2號和3號扇區(實體地址), 佔用兩個扇區大小;
  3. 塊組. 所有的塊會劃分成多個塊組, 每個塊組包含同樣多個塊, 但是可能整個檔案系統整個塊數不是塊組的整數倍, 所以最後一個塊組包含的個數可能會小於其他塊;

總體結構

一個ext檔案系統分成多個塊組, 每個塊組的結構基本相同, 如下:

塊組架構圖

解釋如下:

  1. 0~1號扇區: 引導扇區. 如果沒有引導程式碼, 則這兩個扇區為空, 全部用0填充;
  2. 2~3號扇區: 超級塊, 超級塊包含各種meta資訊:

    1. 塊大小, 每個塊組包含塊數, 總塊數, 第一個塊前保留塊數, inode節點數, 每個塊組的inode節點數
    2. 卷名, 最後掛載時間, 掛載路徑, 檔案系統是否乾淨, 是否要呼叫一致性檢查標識
    3. 空間inode節點和空閒塊的記錄資訊, 在分配inode節點和新塊的時候使用
  3. 組描述符表:

    位於超級塊後面的一個塊, 注意在超級塊之前只佔用了4個扇區, 如果一個塊大小超過4個扇區的話, 這裡就會有空的扇區。

    組描述符表中包含了所有塊組的描述資訊, 每個塊組佔據32個位元組(差不多是8個整數的大小)。如果格式化使用預設引數, 那麼組描述符表不會超過一塊塊組。

    組描述符和超級塊在每個塊組中都有一個備份, 但是激活了稀疏超級塊特徵的情況除外。

    稀疏超級塊即不是在所有塊組中都儲存超級塊和組描述符的備份, 預設該策略被啟用, 其策略類似當塊組號是3,5,7的冪的塊組才存副本。

  4. 塊位圖表

    每個塊對應一個bit, 只包含了本塊組的資訊。而檔案系統建立的時候, 預設每個塊組包含的塊數跟每個塊包含的bit數是一樣的, 這種情況下每個塊位圖表正好等於一個塊的大小。

    而該位圖表的塊的起始位置會在組描述符表中指定, 大小則為塊組中包含塊數個bit。

  5. inode點陣圖塊

    跟塊位圖表類似, 通常情況他也只佔用一個塊。通常一個塊組中的inode節點數會小於block數量, 所以其不會超過一個塊大小。

  6. inode節點表

    每個inode都佔用128位的一個描述資訊, 起始位置在組描述符中表示, 大小為inode節點數*128。

  7. 資料區

    這就是真正儲存資料的區域。

塊大小為4096的時候, 各部分和扇區對應關係如下圖:

4096塊圖

塊大小為2048的時候, 各部分和扇區對應關係如下圖:

2048塊圖

超級塊詳細資訊

超級塊總是位於檔案系統的第1024~2048位元組處。

超級塊中包含的資訊如下:

偏移(16進位制) 位元組數 含義&解釋
00~03 4 檔案系統中總inode節點數
04~07 4 檔案系統中總塊數
08~0B 4 為檔案系統預保留的塊數
0C~0F 4 空閒塊數
10~13 4 空間inode節點數
14~17 4 0號塊組起始塊號
18~1B 4 塊大小(1024左移位數)
1C~1F 4 片段大小, 跟塊大小一模一樣
20~23 4 每個塊組中包含的塊數量
24~27 4 每個塊組中包含的片段數量, 跟包含的快數量一致
28~2B 4 每個塊組中包含的inode節點數量
2C~2F 4 檔案系統最後掛載時間
30~33 4 檔案系統最後寫入時間
34~35 2 當前掛載數
36~37 2 最大掛載數
38~39 2 檔案系統簽名標識 53EF
3A~3B 2 檔案系統狀態, 正常, 錯誤, 恢復了孤立的inode節點
3C~3D 2 錯誤處理方式, 繼續, 以只讀方式掛載
3E~3F 2 輔版本號
40~43 4 最後進行一致性檢查的時間
44~47 4 一致性檢查的間隔時間
48~4B 4 建立本檔案系統的作業系統
4C~4F 4 主版本號, 只有該值為1的時候, 偏移54之後的擴充套件超級塊的一些動態屬性值才是有意義的
50~51 2 預設為UID的保留塊
52~53 2 預設為GID的保留塊
54~57 2 第一個非保留的inode節點號, 即使用者可以使用的第一個inode節點號
58~59 2 每個inode節點的大小位元組數
5A~5B 2 本超級塊所在的塊組號
5C~5F 4 相容標識特徵
60~63 4 非相容標識特徵
64~67 4 只讀相容特徵標識
68~77 16 檔案系統ID號
78~87 16 卷名
88~C7 64 最後的掛載路徑
C8~CB 4 點陣圖使用的運演算法則
CC~CC 1 檔案再分配的塊數
CD~CD 1 目錄再分配的塊數
CE~CF 2 未使用
D0~DF 16 日誌ID
E0~E3 4 日誌inode節點
E4~E7 4 日誌裝置
E8~EB 4 孤立的inode節點表
EC~3FF 788 未使用

塊組描述符詳細資訊

每個塊組描述符佔用32個位元組, 其資料結構如下:

偏移(16進位制) 位元組數 含義&解釋
00~03 4 塊點陣圖起始地址(塊號)
04~07 4 inode節點點陣圖起始地址(塊號)
08~0B 4 inode節點表起始地址(塊號)
0C~0D 2 塊組中空閒塊數
0E~0F 2 塊組中空閒inode節點數
10~11 2 塊組中的目錄數
12~1F - 未使用

inode資訊

每個inode會被分配給一個目錄或者一個檔案, 每個inode包含了128個位元組, 裡面包含了這個檔案的各種meta資訊。

在所有inode中, 1~10號會用作保留給核心使用, 在這些保留節點中, 2號節點用於儲存根目錄資訊, 1號表示壞塊, 8號表示日誌檔案資訊。

第一個使用者可見的都是從11號inode開始, 11號節點一般用作lost+found目錄, 當檢查程式發現一個inode節點已經被分配, 但是沒有檔名指向他的時候, 就會把他新增到lost+found目錄中並賦予一個新的檔名。

inode通過三級指標的方式來找到檔案資料最終儲存的資料塊, 見下圖:

inode查詢檔案塊過程

這種方式能保證小檔案的讀取效率的同時, 也支援大檔案的讀取。

目錄項

在ext檔案系統中, 每個目錄也對應一個inode節點, 該inode節點對應的資料塊裡面會儲存該目錄下所有檔案/目錄的資訊。

每個檔案/目錄對應的資訊就叫目錄項, 目錄項包含的內容也很簡單, 主要就是檔名和指向該檔名的inode指標, 詳細資訊如下:

偏移(16進位制) 位元組數 含義&解釋
00~03 4 inode節點號
04~05 2 本目錄項的長度位元組數
06~06 1 名字長度位元組數
07~07 1 檔案型別
08~ 不定長度 名字的ascii碼

當檔案刪除的時候, 不會真正刪除檔案, 會將該檔案所屬目錄對應的目錄項給刪除, 同時將對應資料塊在快位圖表中標記為0。

連結

  1. 硬連結是指在另外一個目錄對應的目錄項中增加一個目錄項。硬連結建立之後, 使用者無法通過檔名來判斷到底哪個是原檔名, 哪個是連結名;
  2. 軟連線是一種檔案型別, 該檔案裡面就儲存原始檔的完整路徑, 如果原始檔完整路徑長度小於60個位元組, 那麼就將該值直接儲存在inode節點表中, 避免浪費資料塊;

分配策略

  1. 首先判斷應該在哪個塊組中分配
    1. 如果是為檔案建立節點
      1. 預設會在其父目錄所在的組中為其建立節點, 這樣可以確保一個目錄中所有檔案都位於一個大致的區域中
      2. 如果父目錄所在組沒有空閒的節點或者空閒的塊了, 就到別的塊組中為該檔案分配節點, 找尋別的塊組的演算法如下
        1. 每次講當前組號加上2^N次方再求hash
        2. 如果上面的演算法沒找到, 就線性查詢
    2. 如果是為目錄建立節點
      1. 會將其分配到可用空間較多的塊組中, 分配演算法如下:
        1. 首先利用超級塊中的剩餘inode和剩餘塊數字算出每個塊組的平均剩餘數字, 然後依次找到一個大於平均值的塊組
        2. 如果沒找到, 就利用塊組描述符表中的資訊, 找到一個最空閒的塊組
  2. 當一個數據塊分配給某檔案之後
    1. 該塊原來的內容會被請出, inode節點對應的各種元資訊會跟著做修改
    2. 如果是檔案, 節點對應連結數會設定為1, 如果是目錄, 連結數會被設定為2

實際建立/刪除檔案過程

我們建立一個/xuanku/file.txt, 該檔案大小為10000位元組, 檔案塊大小為4096, 那麼下面來看一下過程:

  1. 讀取檔案系統的1024位元組~2048位元組, 即超級塊資訊。通過超級塊得到快大小為4096, 每個塊組含有8192個塊以及2008個inode節點
  2. 讀取檔案系統中第1個塊(即組描述符表), 得到所有塊組佈局資訊
  3. 訪問2號inode節點(即根節點), 通過讀取根節點資料塊資訊, 假設讀到其位於5號塊
  4. 在第5號塊所有目錄項中從前往後遍歷, 直到找到檔名為xuanku的目錄項, 讀到該inode節點為4724
  5. 每個塊組有2008個inode, 用4724針對2008取模, 得到的結果是2號塊組
  6. 通過組描述符表中得到第2個塊組的inode節點表起始於16378號塊
  7. 從16378號塊讀取inode節點表中查詢4724號對應的inode節點(708號表項), 查詢到該xuanku的目錄內容位於17216號塊
  8. 從17216塊讀取xuanku目錄項內容, 將file.txt相關資訊更新到該塊的目錄項當中
  9. 然後開始為檔案分配inode節點, 預設會放到父目錄所在塊組, 即2號塊組, 再次2號塊組的inode節點表16378, 開始從4724往後找到第一個可用的inode節點分配給該檔案, 假設找到的是4850號inode。
  10. 然後就給4850號的inode位圖表設定為1, 組描述符的空閒inode節點數和超級塊的總空閒inode節點數都減1, 將inode節點的地址寫入file.txt對應的目錄項當中, 然後寫各種時間, 記錄日誌
  11. file.txt需要3個塊的儲存空間, 通過2號塊組描述符找到塊點陣圖對應的塊, 並從前往後找到可用的塊。然後將對應的位設定為1, 並更新到inode節點當中
  12. 然後將檔案的內容寫入到對應的塊中

下面再演示一下將該檔案刪除的過程:

  1. 讀取檔案系統的1024位元組~2048位元組, 即超級塊資訊。通過超級塊得到快大小為4096, 每個塊組含有8192個塊以及2008個inode節點
  2. 讀取檔案系統中第1個塊(即組描述符表), 得到所有塊組佈局資訊
  3. 訪問2號inode節點(即根節點), 通過讀取根節點資料塊資訊, 假設讀到其位於5號塊
  4. 在第5號塊所有目錄項中從前往後遍歷, 直到找到檔名為xuanku的目錄項, 讀到該inode節點為4724
  5. 每個塊組有2008個inode, 用4724針對2008取模, 得到的結果是2號塊組
  6. 通過組描述符表中得到第2個塊組的inode節點表起始於16378號塊
  7. 從16378號塊讀取inode節點表中查詢4724號對應的inode節點(708號表項), 查詢到該xuanku的目錄內容位於17216號塊
  8. 從17216塊讀取xuanku目錄項內容, 讀到file.txt的inode節點為4850
  9. 然後取消file.txt的目錄項分配, 修改系列xuanku目錄對應的目錄項資訊
  10. 取消inode節點資訊, 修改2號塊組對應的inode節點表資訊, 將inode節點位設定為0, 更新塊組描述符和超級塊的空閒inode節點數加1
  11. 還要回收檔案內容對應的6個塊空間, 將塊位圖表中的bit設定為0, 清除inode節點的塊指標, 更新塊組描述符和超級塊的空閒塊數加1

參考

  1. 資料重新 檔案系統原理精解與資料恢復最佳實踐
  2. 磁碟分配方式. http://blog.csdn.net/liuqiyao_01/article/details/39156651
  3. ext線上調整大小. http://www.ibm.com/developerworks/cn/linux/l-cn-ext4resize/