檔案系統架構

本文件旨在說明 Fuchsia 檔案系統的概要觀點,包括其初始化、標準檔案系統作業 (例如開啟、讀取、寫入等),以及實作使用者空間檔案系統的方法。此外,本文件說明 VFS 層級行查,命名空間可用於與非儲存空間實體 (例如系統服務) 通訊。

檔案系統是「服務」

不同於更常見的單體式核心,Fuschsia 的檔案系統完全位於使用者空間內。這些元件不會與核心建立連結或載入;純粹是使用者空間程序,實作可能以檔案系統的形式顯示的伺服器。因此,Fusia 的檔案系統本身可以輕鬆變更,不需要重新編譯核心。

檔案系統區塊圖

圖 1:一般檔案系統程序區塊圖。

就像 Fuchsia 上的其他原生伺服器一樣,與檔案系統伺服器互動的主要模式是使用控制代碼原始模式,而非系統呼叫。核心不具備檔案、目錄或檔案系統的知識。因此,檔案系統用戶端無法直接要求核心授予「檔案系統存取權」。

這個架構意味著與檔案系統的互動僅限於以下介面:

  • 透過檔案系統伺服器建立的通訊管道傳送的訊息。這些通訊管道可能是用戶端檔案系統或遠端的本機通訊管道。
  • 初始化處理常式 (應針對個別檔案系統進行大量設定;網路檔案系統需要存取網路,永久檔案系統可能需要封鎖裝置存取權;記憶體內檔案系統只需要配置新的暫存頁面) 的機制。

有了這個介面,任何可透過管道存取的資源,只要實作檔案或目錄的預期通訊協定,就能看起來就像檔案系統。 capabilities

檔案生命週期

建立連線

如要開啟檔案,Fuchsia 程式 (用戶端) 會使用 FIDL 將 RPC 要求傳送至檔案系統伺服器。

FIDL 定義了傳送訊息的傳輸格式,以及檔案系統用戶端與伺服器之間的控制。Fuchsia 程序會將要求傳送至實作「檔案」、「目錄」和「裝置」通訊協定的檔案系統服務,而不會與核心實作的 VFS 層互動。如要傳送其中一個公開要求,Fuchsia 程序必須透過現有處理常式傳送 RPC 訊息至目錄。如要進一步瞭解這項程序,請參閱開放式文件的生命週期

命名空間

在 Fuchsia 上,「命名空間」是完全存在於用戶端的小型檔案系統。就最基本的層面而言,用戶端將「/」儲存為根層級,並將控制代碼與其建立關聯,是個非常原始的命名空間。與其使用一般的「全域」檔案系統命名空間,Fuchsia 程序可以提供任意目錄控制代碼來代表「root」,以限制其命名空間的範圍。為了限制這個範圍,Fuchsia 檔案系統意外不允許透過點號存取父項目錄

Fuchsia 程序可能會將某些路徑作業額外重新導向至不同的檔案系統伺服器。用戶端參照「/bin」時,用戶端可以選擇將這些要求重新導向至代表「/bin」目錄的本機控制代碼,而不是直接將要求傳送至「Root」目錄內的「bin」目錄。如同所有檔案系統建構項目,核心不會從核心看到,而是是在用戶端執行階段 (例如 libfdio) 中實作的命名空間,並且會在大多數用戶端程式碼和遠端檔案系統的控制中心之間穿插。

由於命名空間是在帳號代碼上運作,且大部分的 Fuchsia 資源和服務都能透過帳號代碼存取,因此運作方式極為強大。檔案系統物件 (例如目錄和檔案)、服務、裝置、套件和環境 (可透過特殊權限程序顯示) 全都可以透過控制代碼使用,且可在子項程序中任意組合。因此,命名空間可讓您在應用程式中自訂資源探索。一個程序在「/svc」中觀察的服務,可能不會與其他程序看見的服務一致,並可根據應用程式啟動政策加以限製或重新導向。

如要進一步瞭解哪些機制和政策可以限製程序能力,請參閱沙箱說明文件。

傳送資料

建立至檔案、目錄、裝置或服務的連線之後,後續作業也會透過遠端程序呼叫 (RPC) 訊息傳輸。這些訊息會使用伺服器驗證及解讀的傳輸格式,透過一或多個帳號代碼傳輸。

如果是檔案、目錄、裝置和服務,這些作業會使用 FIDL 通訊協定。

舉例來說,如要在檔案中搜尋,用戶端會傳送內含所需位置的 Seek 訊息,以及 FIDL 訊息中的「whence」(時) 訊息,然後會傳回新的搜尋位置。如要截斷檔案,系統可能會透過新的所需檔案系統傳送 Truncate 訊息,並傳回狀態訊息。如要讀取目錄,系統可能會傳送 ReadDirents 訊息,並傳回目錄清單。如果這些要求傳送至無法處理的檔案系統實體,系統會發生錯誤,而且也不會執行這項作業 (例如傳送至文字檔的 ReadDirents 訊息)。

記憶體對應

就支援此功能的檔案系統而言,記憶體對應檔案比較複雜。如要實際「mmap」檔案的一部分,用戶端會傳送「GetVmo」訊息,並接收虛擬記憶體物件 (VMO) 做為回應。接著,這個物件通常會使用虛擬記憶體位址區域 (VMAR) 對應至用戶端位址空間。如要將檔案內部「VMO」的有限檢視畫面傳輸回用戶端,需要額外的訊息傳遞層進行額外作業,因此請告知對方正在傳回伺服器廠商的物件控制代碼。

藉由傳回這些虛擬記憶體物件,用戶端即可快速存取代表檔案的內部位元組,而不需要實際支付往返 IPC 訊息的費用。這項功能讓 Mmap 能夠為用戶端嘗試在檔案系統互動上進行高處理量的用戶端設計。

對路徑執行的其他作業

除了「開啟」作業之外,還有一些需要討論的「重新命名」和「連結」等以路徑為基礎的運算。與「開啟」不同,這些作業實際上會同時處理多條路徑,而非單一位置。這樣會使其使用方式變得複雜:如果呼叫「rename(‘/foo/bar’, ‘baz’)」,檔案系統必須找出方法才能:

  • 即使兩路徑有不同的起點,也依然可以瀏覽該路徑 (這裡的情況是在這裡;其中一個路徑從根層級開始,另一個則從 CWD 開始)
  • 開啟兩個路徑的父項目錄
  • 同時執行父項目錄和結尾的路徑名稱

為了滿足這種行為,VFS 層會運用名為「Cookie」的 Zircon 概念。這些 Cookie 可讓用戶端作業在伺服器上儲存開啟狀態、使用控制代碼,並於之後使用相同的控制代碼參照。Fuchsia 檔案系統使用此功能來參照其中一個 Vnode,而對另一個 Vnode 執行動作。

這些多路徑作業可以執行下列操作:

  • 開啟父項來源 vnode (「/foo/bar」,亦即開啟「/foo」)
  • 開啟目標父項 vnode (適用於「baz」),並使用 GetToken 作業取得 vnode 權杖,該作業是檔案系統 Cookie 的控制代碼。
  • 將「重新命名」要求、來源路徑和目的地路徑 (「bar」和「baz」) 以及先前取得的 V 節點權杖傳送至來源父項 vnode 要求。這可讓檔案系統安全地間接參照目的地 vnode,如果用戶端提供的控制代碼無效,核心就會拒絕存取 Cookie 的要求,伺服器可能會傳回錯誤。

檔案系統生命週期

固定板

Fshost 負責在系統上掛接檔案系統。在編寫時,變更正在準備讓檔案系統以元件形式執行 (雖然 fhost 仍可控制這些檔案系統的掛接方式)。在可能的情況下,系統會使用靜態轉送。請參閱 fuchsia.fs.startup/Startup 通訊協定。

檔案系統管理

有一組與「管理」相關的檔案系統作業,包括「卸載目前的檔案系統」。這些運算由 admin.fidl 中的 fs.Admin 介面定義。檔案系統會匯出這項服務,同時存取檔案系統的根目錄。

目前的檔案系統

由於 Fuchsia 架構的模組化特性,將檔案系統很容易新增至系統。目前,有一些檔案系統可滿足各種不同的需求。

MemFS:記憶體內檔案系統

MemFS 可用來對臨時檔案系統執行要求 (例如 /tmp),其中檔案完全存在於 RAM 中,且不會傳輸至基礎區塊裝置。此檔案系統目前也用於「啟動檔案系統」通訊協定,其中代表一組檔案和目錄的大型唯讀 VMO 會在啟動時解除包裝,成為使用者可存取的 Vnode (這些檔案可在 /boot 中存取)。

MinFS:永久檔案系統

MinFS 是簡單的傳統檔案系統,能夠永久儲存檔案。和 MemFS 一樣,雖然會利用先前提到的 VFS 層,但與 MemFS 不同,後者需要區塊裝置的額外處理 (系統會在啟動時傳輸至新的 MinFS 程序)。為方便使用,MinFS 還提供多種工具:格式化為「mkfs」、用於驗證的「fsck」、以及可從指令列新增及移除 MinFS 檔案系統的「掛載」。

Blobfs:不可變更、完整性驗證套件儲存檔案系統

Blobfs 是一個簡單的平面檔案系統,已針對「寫入一次,然後唯讀」已簽署的資料最佳化,例如套件。除了兩種小型先決條件 (檔案名稱為確定性、可定址內容雜湊值,用於完整性驗證) 和轉寄檔案大小 (透過呼叫「ftruncate」來識別 Blobfs) 以外,Blobfs 看起來就像典型的檔案系統。這個檔案可以掛接和卸載,包含單一雜湊目錄,而且可透過「開放」、「讀取」、「統計資料」和「mmap」等作業存取 blob。

FVM

Fuchsia Volume Manager 是「邏輯磁碟區管理工具」,可在現有區塊裝置之上提供更多彈性。現行功能包括新增、移除、擴充及縮減虛擬分區。為了實現這些功能,FVM 內部會維持實體與虛擬對應 (虛擬分區、區塊) 到 (切片、實體區塊) 的實體對應。為了將維護負擔降到最低,它允許分區以稱為配量的區塊縮減/增加。配量是原生區塊大小的倍數。除了中繼資料外,裝置的其餘部分會劃分為配量。每個切片可以是免費資料,或屬於一個分區。如果某個配量屬於某個分區,FVM 會維護該分區使用哪個分區的中繼資料,以及該分區內區段的虛擬位址。

FVM 的磁碟上配置如下所示,會在這裡宣告。

      +---------------------------------+ <- Physical block 0
      |           metadata              |
      | +-----------------------------+ |
      | |       metadata copy 1       | |
      | |  +------------------------+ | |
      | |  |    superblock          | | |
      | |  +------------------------+ | |
      | |  |    partition table     | | |
      | |  +------------------------+ | |
      | |  | slice allocation table | | |
      | |  +------------------------+ | |
      | +-----------------------------+ | <- Size of metadata is described by
      | |       metadata copy 2       | |    superblock
      | +-----------------------------+ |
      +---------------------------------+ <- Superblock describes start of
      |                                 |    slices
      |             Slice 1             |
      +---------------------------------+
      |                                 |
      |             Slice 2             |
      +---------------------------------+
      |                                 |
      |             Slice 3             |
      +---------------------------------+
      |                                 |

分區表是由數個虛擬分區項目 (VPartitionEntry) 組成。除了包含名稱和分區 ID,這些 vpart 項目也包含針對這個分區分配的配量數量。

配量分配資料表是由緊密封裝的配量項目 (SliceEntry) 組成。每個項目都包含

  • 分配狀態
  • (如果已配置配置),請
    • 所屬的分區
    • 將分區對應至哪個邏輯切片

您可以在這裡找到 FVM 程式庫。在儲存期間,部分分區會從主機複製到目標。因此分區和 FVM 檔案本身可以在主機上建立。方法是使用這裡的主機端公用程式。使用 fvm-check 進行詳細驗證 FVM 裝置/檔案的完整性。