BlobFS 中的隨機存取壓縮功能

Fuchsia 中的 BlobFS 檔案系統會公開壓縮檔案以節省磁碟空間。BlobFS 支援多種不同的壓縮策略,其中 zstd 為預設值。

檔案壓縮的缺點之一,是可以防止檔案隨機存取。對大部分的壓縮演算法而言,如要存取單一位元組,必須從磁碟讀取整個內容並解壓縮。

在 BlobFS 中,當檔案需要分頁時,這是一項特殊的挑戰。需求分頁可將檔案的一部分載入記憶體,節省系統記憶體。完整檔案壓縮會阻止檔案的一部分載入。

Fuchsia 的「區塊壓縮」格式和程式庫會將壓縮檔案分割為可獨立解壓縮的影格。由於檔案可部分載入和解壓縮,因此 BlobFS 可以實作壓縮檔案的需求分頁。

本文件說明區塊壓縮格式的設計,並說明在 Fuchsia 中使用這種格式。

設計目標和非目標

以下目標可激勵設計區塊壓縮格式和程式庫:

  • 隨機存取解壓縮:必須能夠獨立解壓縮整個檔案,而不必解壓縮整個檔案。

  • 彈性解壓縮 API:這個程式庫可讓用戶端精細控管搜尋資料表,方便用戶端精細控管要解壓縮的影格。這項功能支援需求分頁等用途,其中用戶端 (BlobFS) 含有存取模式的更多相關資訊,並且可透過解壓縮特定影格,以精確的方式控制預先讀取和預先擷取。

    這與較具代管性的 API 不同,後者會隱藏含有未壓縮位元組的影格詳細資料。

  • 可設定的影格大小。您必須能夠調整資料解壓縮架構的大小,以符合不同的用途和不同要求。

  • 彈性壓縮版面配置。此格式支援更多種外語影格版面配置,例如使用不統一的影格大小,或啟動對齊的影格,以便因應日後需要更有彈性的用途。舉例來說,非統一的影格大小可將讀取為較小或較大的影格的資料分組,藉此改善資料位置。

  • 可比較的壓縮率與 zstd。 在撰寫這份文件時,zstd 是 BlobFS 目前的預設壓縮演算法,因此 zstd 的壓縮比率是區塊壓縮程式庫基準測試的基準。因取景以及其他中繼資料而導致的開銷必須極少。

  • 可設定的壓縮積極度。必須能捨棄較低的壓縮速度,以改善壓縮率。

  • 跨平台程式庫。用來壓縮及解壓縮區塊壓縮封存的程式庫必須在 Fuchsia 和編譯主機 (例如 Linux) 上都能使用,這樣才能在建構工具鍊中使用程式庫。您必須在建構期間壓縮檔案,例如產生基本 BlobFS 映像檔時。

以下為非目標:

  • 與 zstd 的格式層級相容性。區塊壓縮封存檔不適合與 zstd 的格式相容,而一般 zstd 工具無法與區塊壓縮封存檔搭配使用。

區塊壓縮

封存格式

區塊封存是由一個標頭組成,後接零個或多個 zstd 框架。

標頭說明封存格式,並包含一個搜尋資料表,用於將壓縮影格對應至解壓縮空間。

      0     1     2     3     4     5     6     7
   +-----+-----+-----+-----+-----+-----+-----+-----+
 0 |                 Magic Number                  |
   +-----+-----+-----+-----+-----+-----+-----+-----+
 8 |  Version  |  Reserved |       Num Frames      |  // Reserved must be zero.
   +-----+-----+-----+-----+-----+-----+-----+-----+
16 |    Header CRC32       |        Reserved       |  // Reserved must be zero.
   +-----+-----+-----+-----+-----+-----+-----+-----+
24 |                    Reserved                   |  // Reserved must be zero.
   +-----+-----+-----+-----+-----+-----+-----+-----+
32 |                                               |
40 |                   Seek Table                  |
48 |                     Entry                     |
56 |                                               |
   +-----+-----+-----+-----+-----+-----+-----+-----+
.. |                                               |
.. |                   Seek Table                  |
.. |                     Entry                     |
.. |                                               |
   +-----+-----+-----+-----+-----+-----+-----+-----+

「標頭 CRC32」是根據整個標頭 (包括搜尋資料表) 計算得出。

跳轉表

每個搜尋資料表項目都會說明壓縮空間中的連續範圍資料,以及該資料展開後的目標解壓縮位置。

   +-----+-----+-----+-----+-----+-----+-----+-----+
 0 |               Decompressed Offset             |
   +-----+-----+-----+-----+-----+-----+-----+-----+
 8 |                Decompressed Size              |
   +-----+-----+-----+-----+-----+-----+-----+-----+
16 |                Compressed Offset              |
   +-----+-----+-----+-----+-----+-----+-----+-----+
24 |                 Compressed Size               |
   +-----+-----+-----+-----+-----+-----+-----+-----+

在壓縮空間中,搜尋資料表項目為「連續」,但在壓縮空間中可能是「不連續」。這可讓您為輸出檔案加入對齊和邊框間距,以提升儲存空間存取效率。

搜尋資料表最多可包含 1023 個項目 (結果產生 32KiB 標頭),且至少必須包含 1 個項目 (產生 64 個位元組的標頭)。一般而言,壓縮的影格會緊接在搜尋表格後面,但這個格式支援搜尋資料表結尾處的任何偏移值。

跳轉表格 (單位為變化版本)
  • I0:第一個搜尋表格項目必須採用解壓縮偏移值 0。

  • I1:第一個搜尋資料表項目的壓縮偏移值必須大於或等於標頭大小。

  • I2:每個項目的解壓縮偏移都必須等於上一個影格的結尾 (即前一個影格的解壓縮偏移 + 長度)。

  • I3:每個項目的壓縮偏移量必須大於或等於上一個影格的結尾 (亦即上一個影格的壓縮偏移值+長度)。

  • I4:每個項目都必須有非零的解壓縮和壓縮長度。

  • I5:所有壓縮過的影格都可能超過檔案結尾。

壓縮畫面

檔案中的每個壓縮頁框都是一般的 zstd 壓縮頁框。指定影格會對應至解壓縮檔案中的一些連續位元組區塊。

系統會忽略檔案未涵蓋的位元組範圍 (搜尋表格未涵蓋的範圍)。

這並不要求每個資料頁框的解壓縮大小都相同,但目前的壓縮方式會將輸入資料分割為大小相等的影格。影格大小可在壓縮期間進行設定。

隨機存取解壓縮

搜尋資料表可用於查詢含有指定解壓縮範圍的影格。收到解壓縮檔案中的指定範圍時,系統必須載入一或多個壓縮影格,並解壓縮。

舉例來說,假設以下檔案有三個影格:

Decompressed Space            Compressed Space
+-----------------+           +--------------+
|                 | <-------- |              |
|                 |           |     +--------|
|                 |           +-----+        |
+-----------------+   +------ |              |
|                 | <-+       +--------------+
|                 |       +-- |              |
|                 |       |   +--------------=
+-----------------+       |
|                 | <-----+
|       +---------|
+-------+

在單一影格中存取位元組範圍時,只需解壓縮對應的壓縮影格:

Decompressed Space            Compressed Space
+-----------------+           +--------------+
|                 | <-------- |xxxxxxxxxxxxxx|
|  xxxxxxxx       |           |xxxxx+--------|
|                 |           +-----+        |
+-----------------+   +------ |              |
|                 | <-+       +--------------+
|                 |       +-- |              |
|                 |       |   +--------------=
+-----------------+       |
|                 | <-----+
|       +---------|
+-------+

橫跨多個解壓縮影格的範圍將需要解壓縮多個壓縮影格:

Decompressed Space            Compressed Space
+-----------------+           +--------------+
|                 | <-------- |xxxxxxxxxxxxxx|
|                 |           |xxxxx+--------|
|              xxx|           +-----+xxxxxxxx|
+-----------------+   +------ |xxxxxxxxxxxxxx|
|xxxx             | <-+       +--------------+
|                 |       +-- |              |
|                 |       |   +--------------=
+-----------------+       |
|                 | <-----+
|       +---------|
+-------+

在 BlobFS 中使用

在 BlobFS 中,檔案會透過區塊壓縮程式庫壓縮,以推動隨機存取和需求分頁。

目前,只有在需求分頁已啟用時,才會使用隨機存取。停用需求分頁後,BlobFS 會在第一次存取時載入並解壓縮整個檔案,在處理檔案時,在記憶體中緩衝處理該檔案。

啟用需求分頁後,BlobFS 會在存取某些檔案時延遲載入這些檔案。BlobFS 會使用 zx_pager_create syscall,將本身註冊為 VMO 的呼叫器。如果在 VMO 中存取非存在的頁面,就會發生頁面錯誤,而 BlobFS 會處理該頁面。

BlobFS 會查詢包含目標網頁的解壓縮影格,然後解壓縮每個影格。壓縮每個影格後,資料會通過驗證,並透過 zx_pager_supply_pages sys 呼叫來修訂支援呼叫的 VMO。