(翻譯 《ZFS On-Disk Specification》, 由於是2006年給出的文檔,與當前ZFS系統肯定有很多的不同,但是也是一份相當有幫助的ZFS學習文檔)
1.1 虛擬設備
ZFS存儲池是由一個虛擬設備集合構成的。這里面一共有兩種虛擬設備:物理虛擬設備(physical virtual devices,也稱為葉虛擬設備,leaf vdevs),以及邏輯虛擬設備(logical virtual devices,也稱為內部虛擬設備,interior vdevs)。物理設備是一個可寫的塊設備(比如一個磁盤);邏輯設備在概念上的一組物理設備。
Vdev是通過一個以物理設備為葉子節點的樹來管理的。每一個pool都有一個被稱為“root vdev”的設備作為這棵樹的根。root節點的所有直接子節點(無論是邏輯的還是物理的)都被稱為top-level vdevs。下圖顯示了一個由兩個Mirror構成的pool的虛擬設備樹。第一個Mirror(標簽為M1)有兩個磁盤,分別通過“vdev A”和“vdev B”來表示;同樣的,第二個Mirror(標簽為M2)也有兩個磁盤,分別通過“vdev C”和“vdev D”來表示。虛擬設備A,B,C,D都是物理虛擬設備。“M1”和“M2”都是邏輯虛擬設備,由於M1和M2都是root的直接子節點,所以都是“top-level vdevs”。
1.2 虛擬設備標簽(Vdev Label)
Pool中的每一個物理虛擬設備都包含一個256K的結構體——vdev_label。這個Label描述了這個設備以及同一top-level vdev下的其他所有虛擬設備的信息。例如:對於虛擬設備C,它的vdev label中包含虛擬設備C,D以及M2的信息。vdev label的詳細信息在下一節中詳細描述。
使用vdev label有兩個目的:它提供訪問pool中信息的入口;另外它還被用於驗證pool的完整新和可用性。為了保證vdev label總是合法可用的,我們使用冗余和特殊的更新方式。冗余:pool中的每個物理虛擬設備都有4個相同的Label,同一個設備上的4份是完全相同的,不同設備則是不同的。更新:Label的更新過程中,使用兩階段的事務來更新,這樣確保虛擬設備上至少有一個合法的Label。下面詳細介紹一下Label的冗余和更新技術。
1.2.1 Label<span "="">冗余
pool中的每個物理虛擬設備都有4份相同的Label,在更新過程中,這4份Label每一個都可以用來訪問、驗證pool中的數據。當一個設備被添加到pool中時,ZFS在設備的最前面放兩個Label,最后面也放兩個Label。下圖顯示四個Label的分布,N表示設備的總大小,L0、L1為前兩個Label,L2、L3為后兩個Label。
<span "="">考慮到一般情況下設備的損壞都是一個連續的段,所以要將Label放在兩個不同的地方。
1.2.2 <span "="">兩階段的事務性更新
Label<span "="">的位置是在設備加入pool時就設定好了,因此Label的更新並不能像ZFS對待其他數據那樣采用Copy-On-Write技術,也就是說,對Label的更新是直接覆蓋原有的數據。但是在寫任何數據的過程中都有可能發生錯誤,為了保證ZFS任何時候總是有一個合法的Label,Label的更新過程被分為兩個階段。第一階段更新偶數的Label(L0、L2),如果在這一階段中任何時刻發生了錯誤,奇數Label(L1、L3)仍然是合法的。在L0、L2更新結束之后,第二階段則是更新L1、L3。
1.3 vdev技術細節
vdev label的信息被分成了4部分:8K的空閑空間(Blank Space),8K的boot header信息,112K的name-value鍵值對以及128K的1K大小的uberblock結構數組。下圖顯示了L0的擴展視圖。
1.3.1 <span "="">空閑空間
ZFS支持使用VTOC(Volume Table of Content)和EFI兩種方式來描述磁盤布局。EFI標簽並不是Slice的一部分(它有自己的保留空間),VTOC標簽必須被寫在Slice0的前8K空間內,所以為了支持VTOC標簽,vdev_label的前8K空間被保留下來防止重寫了VTOC標簽。
1.3.2 Boot Block Header
保留,以后使用。
1.3.3 Name-Value Pair List
后面的112KB存儲描述這個設備以及其關聯設備的鍵值對。關聯設備即:同一個top level vdev下的設備。比如下圖中的灰色圓內部的A,B,M1三個設備。
<span "="">所有的鍵值對都使用XDR encoded nvlists存儲。關於更多的XDR encoding或是nvlists請參見 libnvpair(3LIB)和nvlist_free(3NVPAIR)man手冊。以下的鍵值對都被包含在這112K的部分內。
屬性 |
名稱 |
值 |
描述 |
Version |
version |
DATA_TYPE_UINT64 |
版本號 |
Name |
name |
DATA_TYPE_STRING |
當前vdev所屬的pool名稱 |
State |
state |
DATA_TYPE_UINT64 |
當前pool的狀態,pool所有狀態有: POOL_STATE_ACTIVE 0 POOL_STATE_EXPORTED 1 POOL_STATE_DESTROYED 2 |
Transaction |
txg |
DATA_TYPE_UINT64 |
將這個Label寫入磁盤的事務組號 |
Pool Guid |
pool_guid |
DATA_TYPE_UINT64 |
pool的全局唯一標識符 |
Top Guid |
top_guid |
DATA_TYPE_UINT64 |
top_vdev的全局唯一標識符 |
Guid |
guid |
DATA_TYPE_UINT64 |
當前vdev的全局唯一標識符 |
Vdev Tree |
vdev_tree |
DATA_TYPE_NVLIST |
vdev_tree使用遞歸的方式來描述。下文詳細描述 |
vdev_tree遞歸地描述與當前vdev相關的vdev的信息。
每個vdev_tree都包含以下信息:
名稱 |
值 |
描述 |
type |
DATA_TYPE_STRING |
表明vdev的類, 有以下幾種類型: disk 葉設備:塊設備存儲 file 葉設備:文件存儲 mirror 內部設備:mirror raidz 內部設備:raidz replacing 內部設備:ZFS在設備替換時使用 root 內部設備:vdev tree的根設備 |
id |
DATA_TYPE_UINT64 |
當前vdev在父節點孩子中的序號 |
guid |
DATA_TYPE_UINT64 |
當前樹的全局唯一標識符 |
path |
DATA_TYPE_STRING |
設備路徑(僅用於葉虛擬設備) |
devid |
DATA_TYPE_STRING |
vdev_tree的設備ID,僅用於disk類型的虛擬設備 |
metaslab_ arrary |
DATA_TYPE_UINT64 |
由對象號構成的數組。數組中的每個元素(ma[i])metaslab對應的空間圖的對象號 |
metaslab_ shift |
DATA_TYPE_UINT64 |
log2(metaslab大小) |
ashift |
DATA_TYPE_UINT64 |
log2(當前top level vdev的最小可分配單元,block大小) |
children |
DATA_TYPE_NVLIST_ARRARY |
子節點的vdev_tree |
1.3.4 uberblock
nvlist之后就是uberblock的數組。uberblock是訪問pool中數據的入口。任意時刻,只有一個uberblock處於激活狀態,所有uberblock中事務組編號最高且通過SHA-256 checksum驗證合法的uberblock為激活的uberblock,它類似於UFS文件系統中的超級塊。
為了能夠持續訪問激活的uberblock,激活的uberblock永遠不會被覆蓋。所有對uberblock的修改都是通過寫入uberblock數組中的另外一個元素來完成的。在寫入新的uberblock時,事務組編號以及時間戳都是在同一個原子性的操作中完成。Uberblock通過循環的方式寫入。
下圖顯示兩個uberblock。
uberblock技術細節
uberblock按照機器的字節模式存儲。
ub_magic
用於表示該設備包含ZFS數據。ub_magic的值為0x00bab10c(oo-ba-bloc)
ub_version
版本號,與前文中鍵值對的版本號意義相同。
ub_txg
ZFS中所有的寫操作都是通過事務組來完成的。每個事務組都有一個對應的事務組編號。ub_txg用來表示寫入這個uberblock的事務組編號。ub_txg必須大於等於前面鍵值對中的txg編號。
ub_guid_sum
用來檢驗pool中所有設備的可用性。當pool被打開后,ZFS遍歷pool中所有的葉虛擬設備計算這些GUID的和,然后與ub_guid_sum進行比較,來驗證pool中磁盤的可用性。
ub_timestamp
當前uberblock寫入的時間戳(1970年1月1日至今的秒數)
ub_rootbp
這是一個blkptr結構體,包含了MOS的位置。MOS和blkptr將在后文中詳細描述。
1.4 Boot Block
L0和L1之后有一個3.5M的保留空間。