如果發生系統崩潰,JFS 提供了快速文件系統重啟。通過使用數據庫日志技術,JFS 能在幾秒或幾分鐘之內把文件系統恢復到一致狀態,而非日志文件系統卻要花上幾小時甚至幾天才能完成。本白皮書對 JFS 體系結構作了概述,并且描述了可在 developerWorks 網站上找到的 JFS 技術的設計特性、潛在限制以及管理實用程序。
日志文件系統 (JFS) 提供了基于日志的字節級文件系統,該文件系統是為面向事務的高性能系統而開發的。它具有可伸縮性和健壯性,與非日志文件系統相比,它的優點是其快速重啟能力:JFS 能夠在幾秒或幾分鐘內就把文件系統恢復到一致狀態。
雖然 JFS 主要是為滿足服務器(從單處理器系統到高級多處理器和群集系統)的高吞吐量和可靠性需求而設計的,JFS 還可用于想得到高性能和可靠性的客戶機配置。
體系結構和設計
JFS 體系結構可從磁盤布局特性的角度進行說明。
邏輯卷
所有文件系統討論的基礎是某種類型的邏輯卷。這可以是一個物理磁盤,或物理磁盤空間的某個子集,例如:一個 FDISK 分區。邏輯卷也稱為磁盤分區。
聚集和文件集
文件系統創建實用程序 mkfs,創建了完全包含在分區內的聚集。聚集是包含一種特定格式的磁盤塊陣列,其格式包括超級塊和分配映射表。超級塊將分區標識成 JFS 聚集,而分配映射表描述聚集內每個數據塊的分配狀態。格式還包括描述它所必需的初始文件集和控制結構。文件集是可安裝的實體。
文件、目錄、inode 與尋址結構
文件集包含文件和目錄。文件和目錄由 inode 持續表示;每個 inode 描述文件或目錄的屬性,并作為查找磁盤上文件或目錄數據的起始點。JFS 還使用 inode 來表示其它文件系統對象,如描述文件集中每個 inode 的分配狀態和磁盤位置的映射表。
目錄將用戶特定的名稱映射到為文件和目錄所分配的 inode 上,并且形成傳統的命名層次。文件包含用戶數據,用戶數據中沒有隱含任何限制或格式。也就是說,JFS 將用戶數據看成是未解釋的字節流。根植于 inode 基于盤區的尋址結構用來將文件數據映射到磁盤。聚集超級塊和磁盤分配映射表、文件描述符和 inode 映射表、inode、目錄以及尋址結構一起表示了 JFS 控制結構或元數據。
日志
在每個聚集中維護 JFS 日志,并且用來記錄元數據的操作信息。日志有一種同樣由文件系統創建實用程序設置的格式。聚集內多個安裝的文件集可以同時使用一個日志。
設計特性
JFS 從一開始就設計成完全集成了日志記錄,而不是在現有文件系統上添加日志記錄。JFS 的許多特性使之區別于其它文件系統。
日志處理
JFS 提供了改進的結構化一致性和可恢復性,以及比非日志文件系統(例如:HPFS、ext2 和傳統 UNIX 文件系統)快得多的系統重啟時間。發生系統故障時非日志文件系統容易崩潰,是由于一個邏輯寫文件操作通常占用多個媒體 I/O 來完成,且在任何給定時間,可能沒有完全反映在媒體上。這些文件系統依靠重啟實用程序(也就是 fsck),fsck 檢查文件系統的所有元數據(例如:目錄和磁盤尋址結構)以檢測和修復結構完整性問題。這是一個耗時并且容易出錯的過程,在最糟糕的情況下,它還可能丟失或放錯數據。
相反,JFS 使用原來為數據庫開發的技術,記錄了文件系統元數據上執行的操作(即原子事務)信息。如果發生系統故障,可通過重放日志并對適當的事務應用日志記錄,來使文件系統恢復到一致狀態。由于重放實用程序只需檢查文件系統最近活動所產生的運行記錄,而不是檢查所有文件系統的元數據,因此,與這種基于日志的方法相關的文件系統恢復時間要快得多。
基于日志恢復的其它幾個方面也值得注意。首先,JFS 只記錄元數據上的操作,因此,重放這些日志只能恢復文件系統中結構關系和資源分配狀態的一致性。它沒有記錄文件數據,也沒有將這些數據恢復到一致狀態。因此,恢復后某些文件數據可能丟失或失效,對數據一致性有關鍵性需求的用戶應該使用同步 I/O。
面對媒體出錯,日志記錄不是特別有效。特別地,在將日志或元數據寫入磁盤的期間發生的 I/O 錯誤,意味著在系統崩潰后,要將文件系統恢復到一致狀態,需要耗時并且有可能強加的全面完整性檢查。這暗示著,壞塊重定位是任何駐留在 JFS 下的存儲管理器或設備的一個關鍵特性。
JFS 日志記錄的語義如下:當涉及元數據更改的文件系統操作--例如,unlink()--返回成功執行的返回碼時,操作的結果已經提交到文件系統,即使系統崩潰了也可以發現。例如,一旦成功刪除了文件,即使系統崩潰然后重啟,它仍然是刪除的并且不會再重新出現。
日志記錄風格將同步寫入日志磁盤引入每個修改元數據的 inode 或 vfs 操作。(對數據庫專家而言,這是一種使用非剝奪緩沖區策略的僅重做的、物理殘留映象、提前寫的日志記錄協議。)在性能方面,與依賴(多個)謹慎的同步元數據寫操作以獲得一致性的許多非日志文件系統相比,這種方法較好。但是,與其它日志文件系統相比,它在性能上處于劣勢。其它日志文件系統,如 Veritas VxFS 和 Transarc Episode,使用不同的日志風格并且緩慢地將日志數據寫入磁盤。在執行多個并行操作的服務器環境中,通過將多個同步寫操作組合成單一寫操作的組提交來減少這種性能損失。JFS 日志記錄風格隨著時間推移而得到不斷改進,現在提供了異步日志記錄,異步日志記錄提高了文件系統的性能。
基于盤區的尋址結構
JFS 使用基于盤區的尋址結構,連同主動的塊分配策略,產生緊湊、高效、可伸縮的結構,以將文件中的邏輯偏移量映射成磁盤上的物理地址。盤區是象一個單元那樣分配給文件的相連塊序列,可用一個由 <邏輯偏移量,長度,物理地址> 組成的三元組來描述。尋址結構是一棵 B+ 樹,該樹由盤區描述符(上面提到的三元組)填充,根在 inode 中,鍵為文件中的邏輯偏移量。
可變的塊尺寸
按文件系統分,JFS 支持 512、1024、2048 和 4096 字節的塊尺寸,以允許用戶根據應用環境優化空間利用率。較小的塊尺寸減少了文件和目錄中內部存儲碎片的數量,空間利用率更高。但是,小塊可能會增加路徑長度,與使用大的塊尺寸相比,小塊的塊分配活動可能更頻繁發生。因為服務器系統通常主要考慮的是性能,而不是空間利用率,所以缺省塊尺寸為 4096 字節。
動態磁盤 inode 分配
JFS 按需為磁盤 inode 動態地分配空間,同時釋放不再需要的空間。這一支持避開了在文件系統創建期間,為磁盤 inode 保留固定數量空間的傳統方法,因此用戶不再需要估計文件系統包含的文件和目錄最大數目。另外,這一支持使磁盤 inode 與固定磁盤位置分離。
目錄組織
JFS 提供兩種不同的目錄組織。第一種組織用于小目錄,并且在目錄的 inode 內存儲目錄內容。這就不再需要不同的目錄塊 I/O,同時也不再需要分配不同的存儲器。最多可有 8 個項可直接存儲在 inode 中,這些項不包括自己(.)和父(..)目錄項,這兩個項存儲在 inode 中不同的區域內。
第二種組織用于較大的目錄,用按名字鍵控的 B+ 樹表示每個目錄。與傳統無序的目錄組織比較,它提供更快的目錄查找、插入和刪除能力。
稀疏和密集文件
按文件系統分,JFS 既支持稀疏文件也支持密集文件。
稀疏文件允許把數據寫到一個文件的任意位置,而不要將以前未寫的中間文件塊實例化。所報告的文件大小是已經寫入的最高塊位處,但是,在文件中任何給定塊的實際分配,只有在該塊進行寫操作時才發生。例如,假設在一個指定為稀疏文件的文件系統中創建一個新文件。應用程序將數據塊寫到文件中第 100 塊。盡管磁盤空間只分配了 1 塊給它,JFS 將報告該文件的大小為 100 塊。如果應用程序下一步讀取文件的第 50 塊,JFS 將返回填充了 0 的一個字節塊。假設應用程序然后將一塊數據寫到該文件的第 50 塊,JFS 仍然報告文件的大小為 100 塊,而現在已經為它分配了兩塊磁盤空間。稀疏文件適合需要大的邏輯空間但只使用這個空間的一個(少量)子集的應用程序。
對于密集文件,將分配相當于文件大小的磁盤資源。在上例中,第一個寫操作(將一塊數據寫到文件的第 100 塊)將導致把 100 個塊的磁盤空間分配給該文件。在任何已經隱式寫入的塊上進行讀操作,JFS 將返回填充了 0 的字節塊,正如稀疏文件的情況一樣。
JFS 內部(潛在)限制
JFS 是完全 64 位的文件系統。所有 JFS 文件系統結構化字段都是 64 位大小。這允許 JFS 同時支持大文件和大分區。
文件系統大小
JFS 支持的最小文件系統是 16M 字節。最大文件系統的大小是文件系統塊尺寸和文件系統元數據結構支持的最大塊數兩者的乘積。JFS 將支持最大文件長度是 512 萬億字節(TB)(塊尺寸是 512 字節)到 4 千萬億字節(PB)(塊尺寸是 4K 字節)
文件長度
最大文件長度是主機支持的虛擬文件系統最大文件長度。例如:如果主機只支持 32 位,則這就限制了文件長度。
可移動媒體
JFS 不支持把軟盤作為基本文件系統設備。
標準管理實用程序
JFS 提供創建和維護文件系統的標準管理實用程序。
創建文件系統
這個實用程序提供 mkfs 命令的 JFS 特定部分,用來在指定的驅動器上初始化 JFS 文件系統。該實用程序在較低級別上操作,并假設文件系統所存在的任何卷的創建/初始化由更高級別的另一個實用程序處理。
檢查/修復文件系統
這個實用程序提供 fsck 命令的 JFS 特定部分。該命令檢查文件系統的一致性,修復發現的問題。它也重放日志,把提交的改動應用到文件系統元數據,如果由于日志重放而聲明文件系統是干凈的,就不會再采取進一步操作。如果文件系統不認為是干凈的,這意味著由于某種原因沒有完整和正確地重放日志,或者文件系統不能單靠重放日志來恢復到一致狀態,那么,就對文件系統執行一遍完整檢查。
當執行全部完整性檢查時,檢查/修復實用程序首要目的是要達到可靠的文件系統狀態,以防止將來文件系統崩潰或故障,第二個目的就是面對崩潰時保存數據。這意味著為了達到文件系統的一致性,實用程序可能丟棄數據。具體而言,當實用程序在不做假設的情況下,無法獲得所需信息以將結構上不一致的文件或目錄恢復到一致狀態時,就會廢棄數據。當遇到不一致的文件或目錄時,就廢棄整個文件或目錄,而不再試圖保存任何部分。任何由刪除受損目錄所孤立起來的文件或子目錄,都放在文件系統根下的 lost+found 目錄中。
文件系統檢查/修復實用程序重點考慮的因素之一是所需虛存數量。通常,這些實用程序所需的虛存數量由文件系統的大小決定,這是由于所需虛存主要用于跟蹤文件系統中個別塊的分配狀態。隨著文件系統增大,塊的數量增多,用來跟蹤這些塊所需的虛存數量也隨之增加。
JFS 檢查/修復實用程序的設計區別在于其虛存需求由文件系統中文件和目錄的數量(而不是由塊的數量)所決定。對 JFS 檢查/修復實用程序而言,每個文件或目錄的虛存大約為每個文件或目錄 32 字節,或者對于包含百萬個文件和目錄的文件系統而言,不論其文件系統大小,虛存需求都是大約 32 兆字節。如同所有其它的文件系統,JFS 實用程序需要跟蹤塊分配狀態,但避免使用虛存方法,而是使用位于實際文件系統中的一小塊保留工作區來實現。
日志文件系統如何處理磁盤布局
本文描述磁盤日志文件系統(JFS)布局,以及使用磁盤布局結構來實現可擴展性、可靠性和性能的機制。還會了解用來操作這些結構的策略和算法,以及 JFS 是在哪里使用遍布文件系統的 B+ 樹來提高文件系統操作性能。
JFS 體系結構可通過其磁盤布局特性的上下文進行說明。磁盤布局是 JFS 用來控制文件系統的格式。本文討論盤區的文件幾何構造、目錄格式、塊分配映射表格式、inode 和布局結構的其它特性。本文還提供了文件布局使用的 B+ 樹數據結構的細節和示例。選擇 B+ 樹是為了提高讀寫盤區的性能,這是 JFS 執行的最普通操作。
分區、聚集、分配組、文件集
分區
JFS 文件系統建立在分區上,分區是由 FDISK 導出到 JFS 的抽象。
分區有:
固定分區塊尺寸,其合法值為 512、1024、2048 或 4096 字節。分區塊尺寸定義了分區上支持的最小 I/O 單元。這對應于組成分區的物理設備的基本磁盤扇區大小,最普遍的尺寸是 512 字節。
大小為:PART_NBlocks,是分區磁盤塊數。
分區磁盤塊的抽象地址空間 [ 0.. PART_NBlocks - 1 ]。
聚集
為了支持 DCE DFS(分布式計算環境分布式文件系統),JFS 將磁盤空間分配池(稱為聚集)的概念, 與可安裝的文件系統子樹(稱為文件集)的概念分開。本文中聚集和文件集的術語與其 DFS 用法一致。每個分區剛好只有一個聚集;每個聚集可能有多個文件集。在第一個發行版中,JFS 僅支持每個聚集一個文件集;但是,所有元數據都已設計成適用于所有情況。
聚集有:
在此聚集的開始部分有 32K 保留區域。
固定的聚集塊尺寸,其合法值為 512、1024、2048 或 4096 字節,但不小于分區塊尺寸。聚集塊尺寸定義了聚集上支持的最小空間分配單元。不要把它與分區塊尺寸混淆起來,后者定義的是 I/O 的最小單元。
主聚集超級塊和輔助聚集超級塊。超級塊包含聚集方面的信息,例如:聚集的大小、分配組的大小、聚集塊的尺寸等等,輔助聚集超級塊是主聚集超級塊的直接副本。如果主聚集超級塊損壞,則使用輔助聚集超級塊。這些超級塊位于固定位置。這使得 JFS 不依賴任何其它信息,就能夠找到它們。超級塊結構在 jfs_superblock.h 的 struct jfs_superblock 中定義。
聚集 inode 表,包含描述聚集范圍的控制結構的 inode 。聚集 inode 表邏輯上包含一個 inode 數組。聚集無目錄結構;在聚集或文件集名字空間中,任何地方都沒有聚集 inode 。
輔助聚集 inode 表,包含從聚集 inode 表復制的 inode 。由于對任何文件系統信息的查找而言,聚集 inode 表中的 inode 都是至關重要的,所以它們每一個在輔助聚集 inode 表中都有備份。當然,不會復制 inode 的實際數據,而只是復制可用來查找數據和 inode 本身的尋址結構。
聚集 inode 映射表,描述聚集 inode 表。聚集 inode 分配映射表包含聚集 inode 上及其磁盤位置上的分配狀態信息。
輔助聚集 inode 映射表,描述輔助聚集 inode 表。由于必須復制聚集 inode 表本身,輔助聚集 inode 映射表實際上是與聚集 inode 分配映射表分開的映射結構。
塊分配映射表,描述在聚集內分配和釋放聚集磁盤塊的控制結構。塊分配映射表在聚集磁盤塊內進行一對一映射。
fsck 工作區,它為 fsck 提供用來跟蹤聚集塊分配的空間。因為 JFS 支持超大聚集,所以這一區域是必需的;當 fsck 運行時,可能沒有足夠的內存用來跟蹤內存中的這些信息。超級塊描述了這一區域。每個聚集塊需要一位。 fsck 工作區總是存在于聚集的末端。
內嵌日志為記錄聚集中的元數據更改提供了空間。超級塊描述了這一區域。內嵌日志總是緊跟 fsck 工作空間后。
初始情況下,在聚集創建時分配了第一個 inode 盤區。按需要動態分配和釋放其它 inode 盤區。每個聚集 inode 描述聚集本身的某些方面,如下:
保留聚集 inode 0。
聚集 inode 1,即自身 inode ,描述包括聚集 inode 映射表的聚集磁盤塊。這是一種循環表示法,因為聚集 inode 1 本身也在自己所描述的文件中。可通過強制規定至少第一個聚集 inode 盤區要在眾所周知的位置,即主聚集超級塊后面 4K 的位置,來處理以上顯而易見的循環表示法問題。因此,JFS 能輕而易舉地找到聚集 inode 1,從聚集 inode 1,通過跟隨 inode 1 中的 B+ 樹,能找到聚集 inode 表的余下 inode 。
要復制聚集 inode 表,JFS 還需要找到聚集 inode 1 的副本,以查找所復制表的其余部分。超級塊會包含一個盤區描述符,該描述符描述輔助聚集 inode 表的第一個 inode 盤區的位置。JFS 能夠從中找到輔助聚集 inode 1,以及輔助聚集 inode 表的余下部分。
聚集 inode 2 描述塊分配映射表。
聚集 inode 3 描述安裝時的內嵌日志。雖然分配了 inode ,但無數據存入磁盤。
聚集 inode 4 描述在聚集格式化期間發現的壞塊。在塊映射表中這些標記成已分配。該 inode 是數據為壞塊的普通文件。
保留聚集 inode 5 到 15 以備將來擴展。
從聚集 inode 16 開始,每個文件集有一個 inode ,即文件集分配映射表 inode 。這個 inode 描述了表示文件集的控制結構。當更多文件集添加到聚集中時,為了容納更多的文件集 inode ,聚集 inode 表本身可能必須增大。
分配組
分配組(AG)把聚集中的空間分成大塊,并且允許 JFS 資源分配策略使用眾所周知的方法,來實現更好的 JFS I/O 性能。首先,分配策略嘗試將相關數據的磁盤塊和磁盤 inode 集群起來,使磁盤實現好的局域性。文件通常是順序地讀寫,而目錄中的文件通常一起訪問。其次,為了容納局域性,分配策略嘗試在整個聚集中分配不相關數據。聚集內的分配組用從 0 開始的 AG(分配組)索引。即用 AG 標識。
必須選擇分配組大小,以使 AG 足夠大以不斷提供連續資源分配。為了將聚集擴充或縮小時所需進行的更新數最小化,分配組必須限制最大組數 128。此外,JFS 將對 8192 個聚集塊的分配組大小規定其最小值。分配組大小必須總是 1 個 dmap 頁(1、2、4、8、 ...dmap 頁)描述的塊數的 2 的冪次方。分配組大小在聚集超級塊中存儲。
大小不是分配組大小倍數的聚集將包含部分分配組;磁盤塊沒有完全覆蓋聚集的最后一個分配組。除了JFS 將標記在塊分配映射表中分配的卻不存在的磁盤塊之外,該部分分配組將被當作完整的分配組。
文件集
文件集是文件和目錄的集合,這些文件和目錄形成了可獨立安裝的子樹。文件集完全包含在一個聚集中。請注意,一個聚集中可能有多個文件集;在那種情況下,所有文件集共享由聚集控制結構定義的空閑聚集磁盤塊公共池。
文件集有:
文件集 inode 表,包含描述文件集范圍的控制結構的 inode 。文件集 inode 表邏輯上包含一個 inode 數組。
文件集 inode 分配映射表,描述文件集 inode 表。文件集 inode 分配映射表包含文件集 inode 上及其磁盤位置上的分配狀態信息。描述文件集分配映射表和其他文件集信息的超級 inode ,駐留前面所描述的聚集 inode 表中。由于復制了聚集 inode 表,因此這個 inode 存在第二個版本,它指向同樣的數據。超級 inode 本身是一個文件。當文件集一開始創建時,分配第一個 inode 盤區按需要動態分配和釋放其它 inode 盤區。
文件集中 inode 的分配如下所示:
保留文件集 inode 0。
文件集 inode 1 包含附加的文件集信息,它們無法放入聚集 inode 表中的文件集分配映射表 inode 。
文件集 inode 2 是文件集的根目錄 inode 。注意,JFS 保留了 inode 2 是文件系統的根這一公共 Unix 約定。
文件集 inode 3 是文件集的 ACL 文件。
從文件集 inode 4 開始,文件集 inode 用于一般文件集對象、用戶文件、目錄和符號鏈接。
盤區、inode 、B+ 樹
盤區是當作單元分配給 JFS 對象的連續聚集塊序列。盤區完全包含在一個聚集(并且因此也是在一個分區)中;但是,大盤區可能跨多個分配組。
每個 JFS 對象可用一個 inode 來表示。inode 包含預期的對象特定信息,例如:時間戳和文件類型。它們還包含記錄盤區分配的 B+ 樹。注意,所有 JFS 元數據結構(除超級塊之外)都以文件表示。通過重用這種數據的 inode 結構,數據格式(即磁盤布局) 自然是可擴展的。
盤區、B+ 樹、inode 在以下章節中詳細描述。
盤區
文件是按盤區順序分配的。盤區是當作一個單元分配的聚集塊的連續變長序列。盤區的尺寸范圍是 1 到 2(24)-1 個聚集塊。盤區可能跨越多個分配組(AG)。為了在插入新盤區、定位特定盤區等操作方面有更優性能,這些盤區是按 B+ 樹索引的。
定義一個盤區需要兩個值,即其長度和其地址。長度以聚集塊尺寸為單位計算。JFS 使用 24 位值來表示盤區的長度,因此盤區的范圍大小是 1 到 2(24)-1 個聚集塊。
對于 512 字節的聚集塊尺寸 (所允許的最小值),最大盤區是512*(2(24)-1)字節,(比 8G 稍小)。對于 4096 字節的聚集塊尺寸(所允許的最大值),盤區的最大長度是 4096*(2(24)-1)字節,(比 64G 稍小)。這些限制僅適用于一個的盤區;對整體文件大小沒有限制作用。地址指的是盤區中第一個塊的地址。地址同樣以聚集塊為單位:它從聚集的開始處計算塊偏移量。
結合了用戶特定聚集塊尺寸的基于盤區的文件系統,允許 JFS 不需要單獨支持內部存儲碎片。可配置聚集使用小的聚集塊尺寸(例如,512 字節),以使大量小尺寸文件的聚集內部存儲碎片最小化。
通常,JFS 分配嘗試通過分配最小數量的盤區策略,而使每個盤區盡可能大。這就允許大的 I/O 傳送,結果使得性能提高。然而,對于特殊情況,不一定總有這種結果。例如,一個段的寫入時復制會造成連續盤區被分割成更小的連續盤區系列。另一種情況是盤區大小的限制。例如:由于 JFS 必須把整個盤區讀入內存,然后進行解壓縮,所以壓縮文件盤區大小是有限的。由于 JFS 的可用內存數量有限,因此它必須保證有足夠的空間用于解壓縮盤區。
提供了一個碎片整理實用程序,以減少動態分配/釋放可變長盤區時出現的外部存儲碎片。這種分配和釋放可能導致不相連的變長空閑盤區遍及整個聚集。碎片整理實用程序會把多個小的空閑盤區合并成一個較大的盤區。
inode
JFS 磁盤 inode 是 512 字節。一個 JFS 磁盤 inode 包含 4 組基本信息。第一組描述 JFS 對象的 POSIX 屬性。第二組描述 JFS 對象的其它屬性;這些屬性包括支持 VFS 必需的信息、操作系統環境特定的信息、以及 B+ 樹的頭部。第三組不是包含 B+ 樹根節點的盤區分配描述符就是包含內嵌數據。第四組包含擴展屬性、更多內嵌數據或附加的盤區分配描述符。在 jfs_dinode.h 的 struct dinode 中定義磁盤 inode 結構。
JFS 動態分配 inode 提供的好處如下:
inode 磁盤塊可放在任何磁盤地址,這使得 inode 號和位置分開。這種分離簡化了支持聚集和文件集重組,能夠使聚集縮小。可以移動 inode ,移動后號碼仍然相同。這允許 JFS 不必需要查找目錄結構就可以更新 inode 號。對于支持 DFS 文件集復制而言,這種分離也是必需的。當復制文件集時,僅復制 inode 。既然 JFS 能把新的 inode 放在磁盤的任意位置,新 inode 將有與從它們復制的 inode 相同的號碼。這允許 JFS 不需復制目錄結構并且更新 inode 號。
不再需要分配實際所需十倍的 inode 。這對于 JFS 中較大的 inode 尺寸(大于 512 字節)而言,尤為重要。
大文件的文件分配可能消耗多個分配組且仍是連續的,而靜態分配造成間隔(由于每個分配組中初始分配的 inode )。
另一方面,動態 inode 分配造成大量問題,包括:
對于靜態分配,文件系統的幾何構造隱含描述了磁盤上 inode 的布局;對于動態分配,必需有單獨的映射結構。
對 JFS 完整性而言,這些映射結構是至關重要的。由于復制這些結構的系統開銷,JFS 決定接受丟失這些映射表的風險。但是,JFS 將復制 B+ 樹結構,該結構允許 JFS 查找映射表。
通過只分配磁盤上 inode 連續大塊的 inode 盤區,動態分配了 inode 。根據定義,一個 JFS inode 盤區包含 32 個 inode 。對于 512 字節的 inode 尺寸,因此磁盤上一個 inode 盤區的大小是 16KB。
當分配新的 inode 盤區時,并不初始化盤區。然而,要使 fsck 能夠檢查是否 inode 在使用中,JFS 需要 inode 的一些信息。一旦盤區中的 inode 標記成在使用中,就必須初始化它的文件集號、inode 號、inode 戳以及 inode 分配組塊地址。因此,鏈接字段就足以確定 inode 當前是否正在使用。
注意,動態 inode 分配意味著在 inode 號與 inode 的磁盤地址之間沒有直接關系。因此,JFS 必須有查找磁盤上 inode 的方法。inode 分配映射表提供了這一功能。
inode 生成號只是每當重用 inode 時值就增加的計數器。
存儲每個 inode 生成計數器這一靜態 inode 分配常用方法在動態 inode 分配中不起作用,因為當 inode 空閑時,其磁盤空間可能確實由不是 inode 的數據所重用,(換句話說,空間可能被收回,以存儲普通文件數據)。因此,在 JFS 中,只有一個 inode 生成計數器,它在每一個 inode 分配時增加其值,即在重用 inode 時,相應的計數器增加其值,而不是每個 inode 有一個計數器。
保留聚集 inode 5 到 15 以備將來擴展。
從聚集 inode 16 開始,每個文件集有一個 inode ,即文件集分配映射表 inode 。這個 inode 描述了表示文件集的控制結構。當更多文件集添加到聚集中時,為了容納更多的文件集 inode ,聚集 inode 表本身可能必須增大。
分配組
分配組(AG)把聚集中的空間分成大塊,并且允許 JFS 資源分配策略使用眾所周知的方法,來實現更好的 JFS I/O 性能。首先,分配策略嘗試將相關數據的磁盤塊和磁盤 inode 集群起來,使磁盤實現好的局域性。文件通常是順序地讀寫,而目錄中的文件通常一起訪問。其次,為了容納局域性,分配策略嘗試在整個聚集中分配不相關數據。聚集內的分配組用從 0 開始的 AG(分配組)索引。即用 AG 標識。
必須選擇分配組大小,以使 AG 足夠大以不斷提供連續資源分配。為了將聚集擴充或縮小時所需進行的更新數最小化,分配組必須限制最大組數 128。此外,JFS 將對 8192 個聚集塊的分配組大小規定其最小值。分配組大小必須總是 1 個 dmap 頁(1、2、4、8、 ...dmap 頁)描述的塊數的 2 的冪次方。分配組大小在聚集超級塊中存儲。
大小不是分配組大小倍數的聚集將包含部分分配組;磁盤塊沒有完全覆蓋聚集的最后一個分配組。除了JFS 將標記在塊分配映射表中分配的卻不存在的磁盤塊之外,該部分分配組將被當作完整的分配組。
文件集
文件集是文件和目錄的集合,這些文件和目錄形成了可獨立安裝的子樹。文件集完全包含在一個聚集中。請注意,一個聚集中可能有多個文件集;在那種情況下,所有文件集共享由聚集控制結構定義的空閑聚集磁盤塊公共池。
文件集有:
文件集 inode 表,包含描述文件集范圍的控制結構的 inode 。文件集 inode 表邏輯上包含一個 inode 數組。
文件集 inode 分配映射表,描述文件集 inode 表。文件集 inode 分配映射表包含文件集 inode 上及其磁盤位置上的分配狀態信息。描述文件集分配映射表和其他文件集信息的超級 inode ,駐留前面所描述的聚集 inode 表中。由于復制了聚集 inode 表,因此這個 inode 存在第二個版本,它指向同樣的數據。超級 inode 本身是一個文件。當文件集一開始創建時,分配第一個 inode 盤區按需要動態分配和釋放其它 inode 盤區。
文件集中 inode 的分配如下所示:
保留文件集 inode 0。
文件集 inode 1 包含附加的文件集信息,它們無法放入聚集 inode 表中的文件集分配映射表 inode 。
文件集 inode 2 是文件集的根目錄 inode 。注意,JFS 保留了 inode 2 是文件系統的根這一公共 Unix 約定。
文件集 inode 3 是文件集的 ACL 文件。
從文件集 inode 4 開始,文件集 inode 用于一般文件集對象、用戶文件、目錄和符號鏈接。
盤區、inode 、B+ 樹
盤區是當作單元分配給 JFS 對象的連續聚集塊序列。盤區完全包含在一個聚集(并且因此也是在一個分區)中;但是,大盤區可能跨多個分配組。
每個 JFS 對象可用一個 inode 來表示。inode 包含預期的對象特定信息,例如:時間戳和文件類型。它們還包含記錄盤區分配的 B+ 樹。注意,所有 JFS 元數據結構(除超級塊之外)都以文件表示。通過重用這種數據的 inode 結構,數據格式(即磁盤布局) 自然是可擴展的。
盤區、B+ 樹、inode 在以下章節中詳細描述。
盤區
文件是按盤區順序分配的。盤區是當作一個單元分配的聚集塊的連續變長序列。盤區的尺寸范圍是 1 到 2(24)-1 個聚集塊。盤區可能跨越多個分配組(AG)。為了在插入新盤區、定位特定盤區等操作方面有更優性能,這些盤區是按 B+ 樹索引的。
定義一個盤區需要兩個值,即其長度和其地址。長度以聚集塊尺寸為單位計算。JFS 使用 24 位值來表示盤區的長度,因此盤區的范圍大小是 1 到 2(24)-1 個聚集塊。
對于 512 字節的聚集塊尺寸 (所允許的最小值),最大盤區是512*(2(24)-1)字節,(比 8G 稍小)。對于 4096 字節的聚集塊尺寸(所允許的最大值),盤區的最大長度是 4096*(2(24)-1)字節,(比 64G 稍小)。這些限制僅適用于一個的盤區;對整體文件大小沒有限制作用。地址指的是盤區中第一個塊的地址。地址同樣以聚集塊為單位:它從聚集的開始處計算塊偏移量。
結合了用戶特定聚集塊尺寸的基于盤區的文件系統,允許 JFS 不需要單獨支持內部存儲碎片。可配置聚集使用小的聚集塊尺寸(例如,512 字節),以使大量小尺寸文件的聚集內部存儲碎片最小化。
通常,JFS 分配嘗試通過分配最小數量的盤區策略,而使每個盤區盡可能大。這就允許大的 I/O 傳送,結果使得性能提高。然而,對于特殊情況,不一定總有這種結果。例如,一個段的寫入時復制會造成連續盤區被分割成更小的連續盤區系列。另一種情況是盤區大小的限制。例如:由于 JFS 必須把整個盤區讀入內存,然后進行解壓縮,所以壓縮文件盤區大小是有限的。由于 JFS 的可用內存數量有限,因此它必須保證有足夠的空間用于解壓縮盤區。
提供了一個碎片整理實用程序,以減少動態分配/釋放可變長盤區時出現的外部存儲碎片。這種分配和釋放可能導致不相連的變長空閑盤區遍及整個聚集。碎片整理實用程序會把多個小的空閑盤區合并成一個較大的盤區。
inode
JFS 磁盤 inode 是 512 字節。一個 JFS 磁盤 inode 包含 4 組基本信息。第一組描述 JFS 對象的 POSIX 屬性。第二組描述 JFS 對象的其它屬性;這些屬性包括支持 VFS 必需的信息、操作系統環境特定的信息、以及 B+ 樹的頭部。第三組不是包含 B+ 樹根節點的盤區分配描述符就是包含內嵌數據。第四組包含擴展屬性、更多內嵌數據或附加的盤區分配描述符。在 jfs_dinode.h 的 struct dinode 中定義磁盤 inode 結構。
JFS 動態分配 inode 提供的好處如下:
inode 磁盤塊可放在任何磁盤地址,這使得 inode 號和位置分開。這種分離簡化了支持聚集和文件集重組,能夠使聚集縮小。可以移動 inode ,移動后號碼仍然相同。這允許 JFS 不必需要查找目錄結構就可以更新 inode 號。對于支持 DFS 文件集復制而言,這種分離也是必需的。當復制文件集時,僅復制 inode 。既然 JFS 能把新的 inode 放在磁盤的任意位置,新 inode 將有與從它們復制的 inode 相同的號碼。這允許 JFS 不需復制目錄結構并且更新 inode 號。
不再需要分配實際所需十倍的 inode 。這對于 JFS 中較大的 inode 尺寸(大于 512 字節)而言,尤為重要。
大文件的文件分配可能消耗多個分配組且仍是連續的,而靜態分配造成間隔(由于每個分配組中初始分配的 inode )。
另一方面,動態 inode 分配造成大量問題,包括:
對于靜態分配,文件系統的幾何構造隱含描述了磁盤上 inode 的布局;對于動態分配,必需有單獨的映射結構。
對 JFS 完整性而言,這些映射結構是至關重要的。由于復制這些結構的系統開銷,JFS 決定接受丟失這些映射表的風險。但是,JFS 將復制 B+ 樹結構,該結構允許 JFS 查找映射表。
通過只分配磁盤上 inode 連續大塊的 inode 盤區,動態分配了 inode 。根據定義,一個 JFS inode 盤區包含 32 個 inode 。對于 512 字節的 inode 尺寸,因此磁盤上一個 inode 盤區的大小是 16KB。
當分配新的 inode 盤區時,并不初始化盤區。然而,要使 fsck 能夠檢查是否 inode 在使用中,JFS 需要 inode 的一些信息。一旦盤區中的 inode 標記成在使用中,就必須初始化它的文件集號、inode 號、inode 戳以及 inode 分配組塊地址。因此,鏈接字段就足以確定 inode 當前是否正在使用。
注意,動態 inode 分配意味著在 inode 號與 inode 的磁盤地址之間沒有直接關系。因此,JFS 必須有查找磁盤上 inode 的方法。inode 分配映射表提供了這一功能。
inode 生成號只是每當重用 inode 時值就增加的計數器。
存儲每個 inode 生成計數器這一靜態 inode 分配常用方法在動態 inode 分配中不起作用,因為當 inode 空閑時,其磁盤空間可能確實由不是 inode 的數據所重用,(換句話說,空間可能被收回,以存儲普通文件數據)。因此,在 JFS 中,只有一個 inode 生成計數器,它在每一個 inode 分配時增加其值,即在重用 inode 時,相應的計數器增加其值,而不是每個 inode 有一個計數器。
塊分配映射表
塊分配映射表用來為整個聚集跟蹤分配或釋放的磁盤塊。由于聚集內所有的文件集共享相同的磁盤塊池,在分配或釋放磁盤塊時,聚集內所有的文件集可使用該分配映射表。
塊分配映射表本身是聚集 inode 2 描述的文件。當初始創建聚集時,分配包括聚集空間的映射表數據塊。映射表將隨著聚集的擴充或緊縮而相應動態地增大或縮小。
塊分配映射表跟蹤是否每個個別的聚集塊被分配還是釋放。
映射表的每頁長度為 4K。映射表包含三種類型的頁:bmap 控制頁、dmap 控制頁和 dmap 頁。
每個 dmap 包含表示每個聚集塊的一位。第 i 位表示第 i 個邏輯聚集塊的分配狀態。它由 struct dmap_t 的 jfs_dmap.h 文件定義。每個 dmap 頁包括 8K 的聚集塊。
因為塊分配映射表可能有許多 dmap 頁,它們由 dmap 控制頁組織。這些頁改進了查找空閑塊的大盤區的性能。聚集的大小將決定需要多少頁和多少層。至多有三層,它允許的聚集塊的最大尺寸是 2(43)。如果不是所有層都需要,塊映射表 inode 是每個沒有使用層的第一頁有“洞”的稀疏文件。
JFS 使用提交策略確保控制數據可靠更新。可靠更新意味著一旦系統出錯時,要維持一致的 JFS 結構和資源分配狀態。為了保證塊分配映射表是一致狀態,JFS 維護 dmap 結構中的兩張映射表,工作映射表和持續映射表。工作映射表記錄當前分配狀態。持續映射表記錄提交的分配狀態,由磁盤上找到的或 JFS 日志或提交的 JFS 事務內的記錄描述的分配狀態組成。當釋放聚集塊時,首先更新永久映射表。當分配聚集塊時,首先更新工作映射表。位值為 0 表示空閑資源,值為 1 表示已分配資源。
塊分配映射表的 dmap 控制頁包含與 dmap 結構中樹相似的樹,除葉層包含 1024 個元素外。dmap 控制頁由 struct dmapctl_t 定義。可在 jfs_dmap.h.文件中找到它。
要注意,dmap 結構中的這一字段是一個平面數組,但它表示圖中顯示的樹。樹跟蹤除最底層之外的每層上連續塊的最大號。樹的最底層,從樹 [85] 到樹 [341],包含下面描述的工作映射表的二進制搭檔表示法。樹的其它層包含來自下一較低層的四個部分的最大數目相連空閑塊。
二進制搭檔系統用來完成每個摘要樹的葉層。通過首先為位圖的每個字獲得空閑位的最長二進制搭檔字符串而形成 dmap 結構的樹。字符以 2 的冪編碼,-1 用來表示已分配全部。
然后,使用二進制搭檔系統完成樹的葉。通過取得從指定索引開始、只包括其以 2 的冪顯示的搭檔的最大數目空閑塊,可形成此樹。
請注意,只有完全空閑的字才與其完全空閑的搭檔組合。組合時,最右搭檔變成 -1,以指示它由另一項所表示。
塊分配映射表的 dmap 控制頁包含與 dmap 結構中樹相似的樹,除葉層包含 1024 個元素外。這些元素是樹 [0] 為緊跟下面的 1024 個映射表頁的二進制搭檔表示法。對于 L0 頁,它是接下來的 1024 個 dmap 頁,對于 L1 頁,它是接下來的 1024 個 L0 頁,而對于 L2 頁,它是接下來 1024 個 L1 頁。
在塊分配映射表的頂部,有映射表控制結構 structdbmap_t 。該結構包含摘要信息,能加快查找比平均空閑空間多的 AG。可在 jfs_dmap.h 中找到該結構。
塊分配映射表沒有記日志:它能在恢復期間由 logredo 修復,或者由 fsck 重構。在 fsck 或 logredo 后工作和持續映射表,都必需是相同狀態。
擴展聚集以增大文件系統
要擴展聚集,JFS 必須確保有足夠的頁存儲塊分配映射表, 索引聚集新擴展的塊。通常,從現有的聚集分配空間給新增的頁,但是如果該聚集空間已滿,那就不可能了。所以我們需要解決這種特殊情況。
要解決該問題,通常 JFS 為塊分配映射表分配的空間多于索引聚集地址空間所需的空間。每個映射表都有額外頁空間用于存放位圖,如果該頁指向另一層摘要樹,則該映射表就需額外頁存放所需的摘要信息。這種額外空間使得 JFS 可以在必要時將聚集分為更小的單位,以擴大聚集至所需的大小。擴展聚集,需采取以下步驟:
如果現有聚集的空間足以擴展塊分配映射表,使其能索引新聚集的所有塊,那么,JFS 不做任何特殊處理,將聚集擴展至整個空間。僅當需要考慮聚集將來的擴展時,塊分配映射表才需增加額外頁。
如果沒有足夠空間擴展,那么 JFS 僅給聚集擴展塊分配映射表中已有額外頁所能尋址的塊。
至此,JFS 有一些額外的聚集塊未在聚集中使用到。JFS 可以用這些聚集塊擴大塊分配映射表,以繼續將聚集擴展至所需大小。JFS 必須謹記將這些額外頁放入塊分配映射表中。
這個處理過程完全由 vfs_cntl() 處理,對系統的其它部分隱藏。
另一種表示法:二進制編碼搭檔表示法
塊分配映射表也可以用二進制編碼搭檔系統表示。除了樹的葉結點和 dmap 結構不同外,這種表示法的邏輯和物理結構與前一種一樣。
struct dmap 定義塊分配映射表的最下層。每個 dmap 頁包括 8K 的聚集塊。
/*
* dmap summary tree
*
* dmaptree_t must be consistent with dmapctl_t.
*/
typedef struct {
int32 nleafs; /* 4: number of tree leafs */
int32 l2nleafs; /* 4: l2 number of tree leafs */
int32 leafidx; /* 4: index of first tree leaf */
int32 height; /* 4: height of the tree */
int8 budmin; /* 1: min l2 tree leaf value to combine*/
int8 stree[TREESIZE]; /* TREESIZE: tree */
uint8 pad[2]; /* 2: pad to word boundary */
} dmaptree_t; /* - 360 - */
/*
* dmap page per 8K blocks bitmap
*/
typedef struct {
int32 nblocks; /* 4: num blks covered by this dmap */
int32 nfree; /* 4: num of free blks in this dmap */
int64 start; /* 8: starting blkno for this dmap */
dmaptree_t tree; /* 360: dmap tree */
uint8 pad[1672]; /* 1672: pad to 2048 bytes */
uint32 wmap[LPERDMAP]; /* 1024: bits of the working map */
uint32 pmap[LPERDMAP]; /* 1024: bits of the persistent map */
} dmap_t; /* - 4096 - */
二進制編碼搭檔系統的每一項都有三個字段: type , size 和 bitmap 。 type 字段表示塊空閑、已分配、用位圖表示或不由該字段表示 (don't care)。如果類型是"don't care"則這些塊由左搭檔表示, size 字段忽略。如果 type 是位圖,則位圖字段的 32 位和 32 塊一一對應,表示其空閑或已分配。位值 0 表示空閑塊,1 表示塊已分配。size 是 2 的冪次方,表示該項描述的聚集塊的個數。
對于每個全空閑項,如果其相同大小的左搭檔也完全空閑,則右搭檔設為"don't care"類型,且右搭檔的空間合并入左搭檔。當分配塊時,僅當搭檔分配在同一盤區才合并。必須維護"don't care"類型,以便 logredo 修正映射表。
結構 dmap 包含一個摘要樹。其它每個映射層都包含一個摘要樹。摘要樹提高了查找空閑塊大盤區的性能。摘要信息足以判斷 dmap 頁是否有足夠的空閑位,這樣就無需查看 dmap 頁,從而可以避免無效搜索。
要注意,dmap 結構中的這一字段是一個平面數組,但它表示圖中顯示的樹。樹的每一層都索引最大數目個相鄰的塊。樹的最底層,樹[21]至樹[84],映射至工作映射表中的二進制編碼搭檔表示。樹的其它層包含來自下一較低層的四個部分的最大數目相連空閑塊。塊分配映射表的其它層可能有一個相似的樹,除了葉節點層有 1024 個元素。這些元素映射至樹[0]的二進制編碼搭檔表示,樹[0]指向后面的 dmap 頁。
如果要合并的四個都為"don't care"類型,則合并項大小標記為 -1。這些項的搭檔項負責標記正確的狀態。
inode 分配
動態 inode 分配機制中,inode 號不再直接映射至聚集中特定的邏輯磁盤塊,所以要支持下列三種操作,需要定義新的數據結構:
正向查找: 給定 inode 號,找到磁盤上的 inode 。文件查找是一種典型的正向查找。
反向查找:給定分區磁盤號(更確定,則給定分配組號),查找鄰近的空閑 i-結點。分配新 inode 就屬于這種情況,JFS 盡量查找物理上鄰近所選分配組的 inode (以便,例如, 同一子目錄的文件其 inode 都是相鄰的)。
空閑 inode 號查找:要分配新的 inode 盤區,先要找到 32 個相鄰的、未分配給相應 inode 盤區的 inode 。當所有已分配的 inode 都在使用,或當 JFS 需要給分配組分配 inode 但以前從未分配過 inode 時,或當一個分配組中沒有空閑 inode 時,需要分配新的 inode 盤區。
注意動態 inode 分配的一種微妙效應:相鄰 inode 號在磁盤上未必相鄰:inode N+32 可以和 inode N 相隔任意遠。然而,相隔很遠的 inode 號在磁盤上可以是緊鄰的;所以,inode N+K 和 inode N 緊鄰在理論上是可能的(即使 K>1)
inode 分配映射表
inode 分配映射表解決正向查找問題。聚集和每個文件集都有一個 inode 分配映射表,該表是一個 IAG(inode 分配組)的數組。IAG 是 inode 分配映射表的數據。對于聚集,inode 分配映射表映射的 inode 也稱為聚集 inode 表。對于文件集,inode 分配映射表映射的 inode 也稱為文件 inode 表。
每個 IAG 大小為 4K,描述磁盤上 128 個物理 inode 盤區。由于每個 inode 盤區包含 32 個 inode ,所以每個 IAG 描述 4096 個 inode 。IAG 可以位于聚集的任意位置。IAG 的所有 inode 盤區位于一個分配組,由此 IAG 和 AG 綁定在一起直至釋放所有的 inode 盤區。任意 AG 可以分配空間給一個 inode 盤區,然后該 IAG 就與那個 AG 綁定。IAG 由 struct iag_t 定義(見 jfs_imap.h)。
/*
* inode allocation group page (per 4096 inodes of an AG)
*/
typedef struct {
int64 agstart; /* 8: starting block of ag */
int32 iagnum; /* 4: inode allocation group number */
int32 inofreefwd; /* 4: ag inode free list forward */
int32 inofreeback; /* 4: ag inode free list back */
int32 extfreefwd; /* 4: ag inode extent free list forward */
int32 extfreeback; /* 4: ag inode extent free list back */
int32 iagfree; /* 4: iag free list */
/* summary map: 1 bit per inode extent */
int32 inosmap[SMAPSZ];/* 16: sum map of mapwords w/ free inodes;
* note: this indicates free and backed
* inodes, if the extent is not backed the
* value will be 1. if the extent is
* backed but all inodes are being used the
* value will be 1. if the extent is
* backed but at least one of the inodes is
* free the value will be 0.
*/
int32 extsmap[SMAPSZ];/* 16: sum map of mapwords w/ free extents */
int32 nfreeinos; /* 4: number of free inodes */
int32 nfreeexts; /* 4: number of free extents */
/* (72) */
uint8 pad[1976]; /* 1976: pad to 2048 bytes */
/* allocation bit map: 1 bit per inode (0 - free, 1 - allocated) */
uint32 wmap[EXTSPERIAG]; /* 512: working allocation map */
uint32 pmap[EXTSPERIAG]; /* 512: persistent allocation map */
pxd_t inoext[EXTSPERIAG]; /* 1024: inode extent addresses */
} iag_t; /* (4096) */
inode 分配映射表最前面 4k 大小的頁是控制頁。該頁包含 inode 分配映射表的摘要信息。 dinomap_t 結構的定義見 jfs_imap.h。
邏輯上,inode 分配映射表是動態可擴展的 IAG 結構的數組:
struct iag inode_allocation_map [ 1.. N ];
物理上,inode 分配映射表本身是聚集內的一個文件。聚集 inode 分配映射表由聚集 self-node 描述。文件集 inode 分配映射表由文件集 inode 描述。頁空間的分配和釋放依據 B+ 樹索引需要進行。B+ 樹的鍵是 IAG 頁的字節偏移量。
JFS 使用提交策略確保控制數據可靠更新。可靠更新意味著一旦系統出錯時,要維持一致的 JFS 結構和資源分配狀態。為確保 inode 分配映射表的一致性,每個 IAG 都同時維護兩個映射表,工作映射表和持續映射表。工作映射表記錄當前分配狀態。持續磁盤記錄遞交的分配狀態,包括磁盤上記錄的分配狀態或是 JFS 日志中提交的 JFS 事務記錄描述的分配狀態。
映射表中的每一位記錄相應 inode 是空閑還是已分配的。位值 0 表示 inode 空閑,1 表示 inode 已分配。IAG 的每一個控制區內都有一個摘要映射表,用以提高查找空閑 inode 的性能。摘要映射表映射到 IAG 的工作位圖。摘要映射表使用一位映射工作映射表的相鄰 32 位。每一位表示相應的 inode 可用(0),或相應的 inode 不可用(1)。(如果沒有已分配的盤區,那么該 inode 摘要映射位為 1,表明沒有可用的 inode ,)
IAG 還包含 inode 盤區描述符,該描述符描述相應的 inode 盤區。每個 IAG 有 128 個描述符。IAG 的每個控制區內都有一個摘要映射表,用于改進空閑 inode 盤區查找的性能。摘要映射表用一位映射一個 inode 盤區。0 表示空閑的 inode 盤區,1 表示已分配的 inode 盤區。
如果給定 inode 號,用 inode 分配映射表,通過以下步驟,可以找到 inode 的物理位置:
1. 找到描述該 inode 的 IAG。需要找到 inode 分配映射表在 B+ 樹中的鍵(字節偏移量)。
iag key = ((Inode number / Inodes per iag) * Inodes per iag) + 4096 (EQ 1)
2. 查找已找到的 IAG 中引用的 inode 。這可用于在 IAG 工作映射表和持續映射表中索引。
iag inode index = (Inode number) mod (Inodes per iag) (EQ 2)
3. 查找 IAG 中的 inode 盤區描述符,該描述符描述包含指定 inode 的 inode 盤區。
inode extent descriptor = (iag inode index) / (Inode per inode extent) (EQ 3)
4. 要找的 inode 位于找到的 inode 盤區內、適當的偏移量處。
inode offset = ((iag inode index) mod (Inodes per inode extent)
* sizeof dinode) (EQ 4)
inode 分配映射表本身由聚集 inode 表中文件集的分配映射表 inode 描述。
通過前面介紹的公式,將 inode 號,#9157,轉換成一個偏移量:
iag key = ((inum / num_inodes_per_iag ) * (num_inodes_per_iag )) + 4096
= ((9157 / 4096) * 4096 ) + 4096
= 12288
iag inode index = inum mod num_inodes_per_iag
= (9157 mod 4096 )
= 965
inode extent descriptor = iag_inode_index / num_inodes_per_extent
= 965 / 32
= 30
inode offset = (iag_inode_index mod num_inodes_per_extent)
* sizeof dinode
= (965 mod 32) * 512
= 5 * 512
= 2560
為簡化 JFS 維護命令,及便于理解布局策略的動態性,inode 分配映射文件盤區的大小總為 4KB。
當新文件集創建時,必須分配一個 IAG 以及第一個 inode 盤區,以處理文件集的元數據文件。(即,保留的 inode 和根目錄 inode )。
AG 空閑 inode 列表
AG 空閑 inode 列表解決反向查找問題。為減少擴展和縮減聚集的系統開銷,JFS 設定每個聚集允許的最大 AG 數。所以,AG 空閑 inode 列表頭的個數是固定的。列表頭在 inode 分配映射表的控制頁中。表的第 i 項是一個雙向列表的頭,表的第 i 項是一個雙向列表的頭,該雙向列表是第 i 個 AG 中的所有包含空閑 inode 的 inode 分配映射表項(IAG)的集合。IAG 號作為列表索引。-1 表示列表尾。每個 IAG 控制區都包含指向該列表的正向和反向指針。
AG 列表從表頭開始插入。當分配新的 inode 盤區,或當因盤區占滿而刪除一個 inode 時,會有插入操作。當一個 IAG 所有的 inode 盤區都滿時,從列表中刪除該 IAG。
注意 AG3 中的 IAG 沒有任何相應的 inode 盤區可供分配。所以,這些 inode 未在 AG 空閑 inode 列表中表示。
此表沒有記日志;但可以在恢復時由 logredo 恢復,或由 fsck 重建。AG 空閑列表結構定義是 struct dinomap_t,見 jfs_imap.h 文件。
AG 空閑 inode 盤區列表
AG 空閑 inode 盤區列表有助于解決反向查找問題以及空閑 inode 號查找問題。這使得 JFS 能找到下一個空閑盤區所在的 IAG 號和 AG 號。(實際是給出了空閑 inode 號。)每個文件集的每個 AG 都有一個AG 空閑 inode 盤區列表。為減少擴展和縮減聚集的系統開銷,JFS 設定每個聚集允許的最大 AG 數。所以,AG 空閑 inode 盤區列表頭的個數是固定的。列表頭在 inode 分配映射表的控制頁中。表的第 i 項是一個雙向列表的頭,該雙向列表是第 i 個 AG 中所有包含空閑 inode 的 inode 分配映射表項(IAG)的集合。IAG 號作為列表索引。-1 表示列表尾。每個 IAG 控制區都包含指向該列表的正向和反向指針。
當盤區中所有的 inode 都已刪除,則釋放該 inode 盤區的磁盤塊。當 IAG 的一個 inode 盤區被刪除時,該 IAG 插至所屬的 AG 空閑 inode 盤區列表的表頭。當創建新的 IAG,并分配一個 inode 盤區時,該 IAG 號插至 AG 空閑 inode 盤區列表的表頭。當 IAG 的所有 inode 盤區分配完時,從列表中刪除該 IAG。當釋放 IAG 的所有 inode 盤區時,從列表中刪除該 IAG 同時加到IAG 空閑列表中。當 AG 需要分配 inode 盤區時, 則使用 AG 空閑列表頭上的第一項。
此表沒有記日志;但可以在恢復時由 logredo 恢復,或由 fsck 重建。
表的結構定義見 jfs_imap.h, struct dinomap_t .
IAG 空閑列表
IAG 空閑列表有助于查找空閑 inode 號。這使得 JFS 不用查看相應分配的 inode 盤區就可找到 IAG。(實際時給出了空閑 inode 號)。聚集和其每個文件集都有自己的鏈表。該列表的每個項指向一個 IAG 鏈表。IAG 號作為列表索引。-1 表示列表尾。當刪除盤區的所有 inode 時,則釋放該 inode 盤區的磁盤塊。如果某個 IAG 的所有 inode 都為空閑,則該 IAG 號插入 IAG 空閑列表頭。當需要分配新的 inode 盤區,而該 AG 中又沒有包含空閑盤區的 IAG,則使用 IAG 空閑列表頭的第一項(即從表中刪除)。inode 盤區分配描述符一經分配就不再刪除。inode 盤區的地址設為 0x0。
對于聚集 IAG 空閑列表頭是聚集自用 inode 的一個字段。對于每個文件集 IAG 空閑列表頭是文件集分配映射表 inode 的一個字段。該列表沒記日志;但可在恢復時由 logredo 修復,或由 fsck 重建。
IAG 空閑列表的結構定義 struct inomap_t 在文件 jfs_dinode.h 中。
下一個空閑 IAG
下一個空閑 IAG 計數器有助于查找空閑 inode 號。使得 JFS 能找到下一個可以分配的 IAG 的 iag號。(實際是讓 JFS 找到空閑 inode 號)。聚集和其每個文件集都有自己的計數器。計數器在 inode 分配映射表的控制頁中。IAG 一經分配就不再刪除。
文件集分配 inode
文件集 inode 表中的文件集分配映射表 inode 是特殊類型的 inode 。既然這些節點表示文件集,則可以說是文件集的“父 inode ”。這些節點包含文件集特定信息,而不是一般的 inode 數據。同時也記錄文件集 inode 分配映射表在 B+ 樹中的位置。結構定義 struct dinode 見文件 jfs_dinode.h
文件
文件由包含一個 B+ 樹根的 inode 表示,B+樹描述包含用戶數據的盤區。B+ 樹以盤區的偏移量作為索引。
符號鏈接
符號鏈接由一個 inode 表示,該 inode 的 di_mode 字段設置為符號鏈接模式 (S_IFLNK)。如果 inode 內有空間,則鏈接文件的整個路徑直接存儲在 inode 中。否則,將作為 inode 的數據存于盤區中(通過該 inode 的 B+ 樹索引)。
目錄
目錄是 JFS 中日志化的元數據文件。目錄由目錄項組成,目錄項表示目錄中包含的對象。目錄項將名字和 inode 號連接在一起。特定的 inode 描述特定名字的對象。為提高目錄項定位的性能,B+ 樹采用按名排序。
目錄 inode 的 di_size 字段僅表示目錄 B+ 樹的葉子頁。如果 inode 中包含目錄的葉節點,則 di_size 字段為256。
目錄中沒有特定項表示自身 (".") 和父目錄 ("..")。而在 inode 中表示。自身就是目錄自己的 inode 號。父目錄是 inode 中的特殊字段, idotdot,struct dtroot_t ,見文件 jfs_dtree.h。
目錄 inode 包含 B+ 樹的根,處理方法和一般文件類似。只是目錄 B+ 樹以名為鍵。目錄 B+ 樹的葉節點包含目錄項,且以目錄項的全名作為鍵值。目錄 B+ 樹最下層內部節點使用后綴壓縮。其它內部節點采用相同的壓縮后綴。后綴壓縮將名字縮至最短,正好足以區分當前目錄項和前一目錄項。
由于 B+ 樹項的大小是可變的,JFS 需要處理這些項的方案。JFS 想要避免在刪除一項時引起的項移動,平均一項有2K的數據。
B+ 樹節點的內容:
固定個數的目錄槽,個數取決于節點的大小。這些槽用于存儲目錄槽數組和目錄項或路由項。目錄槽的大小總是 32 字節。固定大小的目錄槽使得 JFS 在刪除目錄項不必移動,從而還避免了內部碎片。
一個目錄 B+ 樹的頭,描述 B+ 樹 inode 。此部分包含一個標志,標記節點是內部節點或是葉節點, 及是不是 B+ 樹的根節點。還包含自身的塊地址。 nextindex 字段記錄目錄槽數組中的最后一項。 stblindex 字段記錄目錄槽數組的開始位置。 freelist 字段指向該節點中空閑槽列表頭。
一個目錄槽數組,它是正使用的目錄槽索引的有序數組。使用該數組減少了目錄項增刪時所需的移動次數。數組比項本身小很多,所以移動的只是數組而不是整個項。在數組中,可以用二分法搜索某個目錄項。
一個目錄 B+ 樹槽空閑列表,使得內部碎片最小化。目錄 B+ 樹的頭包含列表表頭,每個空閑目錄槽指向列表中的下一個空閑槽。如果有一系列相連的空閑槽,則在第一個槽中設立一個計數值,說明該系列的長度。這有利于在新建目錄 B+ 樹節點時,進行快速初始化。
一個目錄項,將名字鏈接到一個 inode 號。目錄項包含在葉節點的目錄槽中。如果需要存儲整個目錄名,目錄項可以有附加槽。目錄項的 next 字段表明該項是否有后繼項。大多數目錄項只有單個槽。
一個路由項,用于記錄目錄 B+ 樹的搜索路徑。路由項包含在內部節點的目錄槽中。路由項將按后綴壓縮的路由鍵映射到盤區,此盤區包含下一層目錄 B+ 樹的內部節點或葉節點。如果路由項需要記錄整個的路由鍵,則可以有附加槽。路由項的 next 字段表示該項是否有后繼項。大多數路由項只有單個槽。
目錄 B+ 樹中的內部節點或葉節點是 4K 大小的頁。由于許多目錄都不是很大,所以這種方式對大多數目錄來說是很浪費磁盤空間的。所以目錄的初始葉節點采用以下分配方案:
初始目錄項存儲在目錄嵌入數據區中。
當目錄 inode 的嵌入數據區填滿時,JFS 分配一個葉節點,大小和聚集塊的尺寸一樣。
當初始葉節被占滿,而大小又不到 4k,則倍增節點大小。首先在當前盤區中擴增;如果沒有足夠空間,則需分配新的盤區,然后將舊盤區的數據復制到新盤區。目錄槽數組僅夠存放頁未擴時的槽,所以必須創建新的槽數組。從新分配的數組起始處使用槽,并將舊的數組數據復制到新的位置。更新指向該數組的頭指針,并將舊數組中的槽添加到空閑列表中。
如果葉節點再次填滿,而大小仍不足 4K,重復步驟 3。一旦葉節點達到 4K 則分配新葉節點。初始節點后的每個葉節點,一開始就分配 4K。
當葉子頁的所有項都釋放,則從 B+ 樹中刪除該頁。僅當目錄中所有目錄項都已刪除,目錄又縮回 inode 。
訪問控制列表 (ACL)
JFS 的每個 inode 都有不同的訪問控制列表 (ACL)。ACL 可以表示不同的項,例如許可權、用戶標識符、或組標識符。聚集 inode 的 ACL 字段是沒有用的。
雖然在磁盤上和內存中 ACL 的表示方式沒有規定,但從 DFS 外部所看到的“外部”表示是固定的。ACL 大小的唯一限制是其外部表示必須適合 8192 字節大小的 dfs_acl 結構。
任意 JFS 對象都可有一個管理該對象存取的 ACL;這種 ACL 稱為常規 ACL。目錄對象在創建時可能用到兩個關聯的可選 ACL;初始目錄 ACL和初始文件 ACL。初始 ACL 的作用范圍是目錄中的所有文件。
ACL 體系結構未指定 ACL 的存儲方式,但建議 ACL 有字段標識或命名其輔助對象,這樣通過簡單的等同性檢查就可以檢測到文件集中的共享關系。因此,JFS 在每個文件集中用一個文件(ACL 文件)存儲文件集的 ACL;文件集 inode 1 就是 ACL 文件。文件集中的每個 inode 在 ACL 文件中存放一個索引。
ACL 文件需要一個存儲 ACL 空閑區域的位圖。ACL 文件有一個 4K 大小的位圖,標識 8M 的 ACL 項,如有必要可擴增。位圖中的一位代表 256 字節連續磁盤空間;位圖不描述自身的狀態。
ACL 文件的數據未日志化。
擴展屬性(EA)
擴展屬性是附加到 JFS 對象適用存儲和存取的機制。EA 連續存儲在擴展屬性空間 (EAS) 中,空格存儲 EAS 由 JFS 對象 inode 的 EA 描述符定義。EA 描述符只是一個盤區描述符,定義見 jfs_types.h, struct dxd_t 。
EA 可以存放在 inode 內,或存放在單獨盤區內。EA 描述符的標志字段指示存儲的方式。由于此空間也可用于存放文件 xtree 附加的 xad 項,所以 inode 的 di_mode 字段指明該空間是否可用。如果該字段值為 INLINEEA,則表明空間可用。
如果 EA 存于 inode 內,則忽略 EA 描述符的 offset 和 length 字段。EA 描述符的大小表示數據的字節數。
如果 EA 存于盤區內,EA 描述符將描述該盤區。JFS 不希望 EA 數據太大,所以 JFS 不支持每個 inode 有多于一個盤區的 EA 數據。
EA 項包括 EA 名稱和其值。要訪問某個 EA,JFS 只是線性搜索 EA 數據。
EA 數據未日志化,但它是寫同步的(即數據不是舊數據,就是新數據,但絕不可能是部分更新的數據)。JFS 在日志中記錄 EA 數據的位置。嵌入 EA 數據是日志化的。
流
流用于將數據連接到一個文件或目錄。這種附加數據和目錄數據相似,都可按名引用。在第一版中不支持流,在這里討論僅為元數據結構的完整性。
磁盤 inode 的四部分的第二部分有一個字段描述流描述符。由于附加到一個對象的流數目是可變的,所以流描述符是一個 inode 號,以允許流增加或縮減。流描述符 inode 指向的數據稱為流列表。
流沒有關聯的擴展屬性,所以從不使用流的 inode 四部分的最后一個部分-擴展屬性。實際上該部分用于附加的流項。B+ 樹的數據如同目錄項。每個流都有自己的 inode ,它們依次記錄流數據存放的數據塊地址。
結束語
JFS 小組最重要的目標是創建可靠的,高性能的文件系統。本文討論了 JFS 磁盤布局結構,以及實現可伸縮性、可靠性和高性能的機制。同時詳細探討了 JFS 如何在整個文件系統中使用 B+ 樹提高文件系統操作。