開啟 'Open' 的生活

為提供 Fuchsia 檔案系統存取權的端對端樣貌, 就會深入介紹執行特定動作時使用的每一層 就像開啟檔案一樣簡單請特別注意,這些圖層都位於 userspace;即使與檔案系統伺服器和驅動程式互動 只用來在元件之間傳遞訊息

通話對象:

open(“foobar”);

存取要求的位置

標準程式庫:開啟位置的定義

「開啟」呼叫是由標準程式庫提供的函式。適用對象 C/C++ 程式,這通常會在 unistd.h 中宣告,且其具備 libfdio 中的備份定義。 就 Go 程式而言,Go 中會提供相同 (但截然不同的) 實作項目 標準程式庫。開發人員可以選擇針對每種語言和執行階段選擇應用程式 ID

在單體式核心中,open 是系統周圍的輕量墊片 呼叫,因此核心可能會處理路徑剖析、重新導向等作業。 因此核心需要根據外部 IP 位址 調整資源存取權 有關來電者的知識不過,Zircon 核心 並未刻意設定 以及這類系統呼叫而應改為透過管道存取檔案系統 -- 程序初始化時,會提供「命名空間」。 計算的是「絕對路徑」資料表->「把手」對應。所有已存取的路徑 處理程序中,透過此命名空間向要求開啟 對應。

但在本例中,要求開啟「foobar」(相對路徑) 因此系統可能無法透過代表 目前的工作目錄 (本身以絕對路徑表示) 和帳號代碼)。

標準程式庫負責處理帳號代碼 (或多個帳號代碼) 並使它們看起來像檔案描述元因此,「 「描述元表格」是存在於用戶端程序中 ( 選擇使用自訂執行階段時,他們只看得到 帳號代碼,可視需要選擇「檔案描述元」包裝)。

但這引發了一個問題:針對檔案、通訊端, 管道等等,標準程式庫要如何製作所有這些資源 功能是否一樣呢?客戶如何得知要傳送哪些訊息 可以使用這些控制代碼嗎?

Fdio

名為 fdio 的程式庫 負責提供整合式介面給各種資源 檔案、通訊端、服務、管道等這個資料層會定義 可能會用到的函式,例如讀取、寫入、開啟、關閉、跳轉等 由各種通訊協定支援的檔案描述元。每個支援的通訊協定都是 負責提供用戶端程式碼 互動。例如,通訊端為用戶端提供多個控制代碼;One 和控制層一樣相較之下,「檔案」 通常只會使用一個管道來控管資料和資料 (除非進行額外工作,否則 才能要求進行記憶體對應)。雖然通訊端和檔案可能 接收對 openwrite 的呼叫,因此需要解讀這些指令 有差異。

為方便說明,在本文中,我們會以主要通訊協定 檔案系統用戶端所用的 ID:FIDL

FIDL

呼叫 open("foo") 的程式會呼叫標準程式庫。 找到與目前工作目錄相對應的「fdio」物件, 則需要向遠端伺服器傳送要求,才能「請打開 foo」我們要怎麼做 如何完成?這項計畫提供下列工具:

  • 一或多個代表 CWD 連線的處理常式
  • zx_channel_write: 可傳送位元組和處理 (透過管道) 的系統呼叫
  • zx_channel_read: 可接收位元組和處理 (透過管道) 的系統呼叫
  • zx_object_wait_one: 可等待帳號代碼可讀取 / 寫入的系統呼叫

用戶端可以利用這些基元,撰寫訊息至檔案系統伺服器 CWD 控點上,伺服器可以讀取並回應 回信給客戶「成功」或「失敗訊息」。伺服器的同時 釐清該開放哪些元素 因為在嘗試讀取狀態訊息之前,可能不會選擇等候。

用戶端和伺服器必須就這些協議 傳送或接收訊息時,N 個位元組和 N 會處理:如果有 否則可能遭到捨棄 (更糟的是, 或非預期的行為)。此外,如果這個通訊協定允許用戶端 且完全掌控伺服器,通訊層就會成熟 加以利用

FIDL IO 通訊協定 是指這些位元組和處理代碼的實際意義 會發生這種情形這個通訊協定會說明 「預計的帳號代碼數量」、「列舉作業」和「資料」。在這個範例中 open("foo") 會建立 Open 訊息並設定 FIDL 的「資料」欄位 訊息傳送給「foo」字串此外,如果將任何旗標傳遞至 開啟 (例如 O_RDONLY, O_RDWR, O_CREAT 等) 就會放在 FIDL 結構的「arg」欄位。不過,如果作業已變更 (例如 write),則這則訊息的解讀方式會是 變造。

這層須確切的位元組協議很重要,因為這能讓通訊 相差甚遠的執行階段:瞭解 FIDL 可以 在 C、C++、Go、Rust 和 Dart 程式等之間輕鬆通訊 公開透明。

libfidl 包含 C/C++ 的用戶端和伺服器端程式碼 並負責自動驗證輸入內容 兩端的輸出內容

open 作業而言,FIDL 通訊協定會預期 用戶端會建立一個管道,並將一端 (做為控制代碼) 傳遞至伺服器。一次 交易完成後,這個管道可能會用來做為 與開啟的檔案進行通訊 與「CWD」控制代碼通訊

設計通訊協定,讓 FIDL 用戶端提供控點,而非伺服器, 這種通訊更適合引導線FIDL 物件的存取權可能 非同步;可將要求傳送至 FIDL 物件的要求,然後再傳送 會有什麼影響?這種行為十分重要 功能 代管者 元件 - 元件 (預設為) 延遲啟動 當能力要求 (透過公開呼叫完成) 時。而不是 封鎖元件,並在元件完成啟動程序並開始 這個模型可讓用戶端開始傳送要求 然後,系統會在元件就緒時做出回應。詳情請見 開啟通訊協定的生命週期。 能力轉送

總結來說,「開放式」呼叫會通過標準程式庫,以 「CWD」fdio 物件,用來將要求轉換成 FIDL 訊息, 則會透過 zx_channel_write 系統呼叫傳送至伺服器。客戶可以 您可以選擇使用 zx_object_wait_one 等待伺服器回應,也可以繼續操作 以非同步的方式處理資料不論是哪一種方式,管道都已經建立完成 其中一個端在用戶端運作,另一端會傳送至 「server」。

檔案系統:伺服器端

調度

客戶端發送訊息之後 都存在於管道的伺服器端等待讀取。伺服器是 只要「握手握在管道另一端的帳號代碼」 就能識別出這個帳號代碼 可能會與用戶端處於相同 (或不同) 程序,因此使用相同的 (或 執行階段不同,並以相同 (或不同的) 執行階段寫入 語言) 而非用戶端。只要使用商定的傳輸格式, 處理序間依附元件的瓶頸,在於該通訊層 而非管道

在未來某個時間點,這個 CWD 帳號的伺服器端將需要 讀取用戶端傳輸的訊息。這項程序不會自動進行 -- 伺服器需要刻意等待 接收控制代碼,在本例中為「目前工作目錄」 控制代碼。開啟伺服器物件 (檔案、目錄、服務等) 時, 其控制方式是透過伺服器端的 Zircon 通訊埠註冊, 其基礎控制代碼設為「可讀取」 (表示郵件已送達) 或 已關閉 (代表使用者永遠不會再收到訊息)。這個物件 將傳入要求分派到適當帳號代碼,稱為「調度工具」。 負責將傳入訊息重新導向至回呼函式。 以及先前提供的「iostate」代表開放式連線

針對使用 libfs 的 C++ 檔案系統,系統會呼叫這個回呼函式 vfs_handler,然後會接收下列幾項重要資訊:

  • FIDL 訊息,由用戶端提供 (或人為建構) 如果帳號代碼關閉,伺服器將顯示為「關閉」訊息)
  • 代表目前與帳號代碼連線的 I/O 狀態 (以 之前提到的「iostate」欄位)

vfs_handler 可以解讀 I/O 狀態來推斷其他資訊:

  • 檔案內的搜尋指標 (如果已使用 readdir,就會位於 目錄中)
  • 用來開啟基礎資源的旗標
  • 代表基礎物件的 Vnode 檔案 (可在兩個節點之間共用) 多個用戶端或多個檔案描述元)

這項處理常式功能可以與 「switch/case」表格,將 FIDL 訊息重新導向至適當的函式 則視用戶端提供的「Operation」欄位而定在未解決的情況下, 系統會留意 Open 序數做為作業,因此 (1) 預期的是控制代碼, (2)「data」欄位 (「foo」) 會被解讀為路徑。

VFS 層

在 Fuchsia 中,「VFS 層」是與檔案系統無關的程式碼程式庫, 可能會分派及解讀伺服器端訊息,以及在 基礎檔案系統值得一提的是 選用) 表示檔案系統伺服器不想連結至此程式庫。 沒有義務使用檔案系統伺服器 只瞭解 FIDL 線路格式。因此 相同語言中的任意數量的「VFS」導入作業。目前有這些 實作:

  • In-tree C++ VFS:供 Fuchsia 的「main」使用檔案系統的 Minf blobfs。這個平台目前具備所有 VFS 導入作業中最多的功能,但也可能是 難以使用
  • In-tree Rust VFS:部分 Rust 檔案系統會使用這項目,包括 Fat32 實作練習此為更新版本,目前提供的功能比 C++ 少 。
  • SDK C++ VFS:簡化版的「樹狀結構內」版本C++ 版本 這項工具專為 SDK 使用者設計最常用於較簡單的用途,例如服務探索。

VFS 層會定義可轉送至 基礎檔案系統,包含:

  • 讀取/寫入 Vnode
  • 從父項 Vnode 查詢/建立/取消連結 Vnode (按照名稱)
  • 依名稱重新命名/連結 Vnode
  • 其他應用程式

實作檔案系統 (假設開發人員想使用共用 VFS) 只需定義實作這個介面與連結的 Vnode 即可 與 VFS 層相比的結果即可啟用「步行路徑」和 可以輕鬆「掛接檔案系統」,而且幾乎不會出現重複的程式碼。在 要維持各檔案系統通用,VFS 層沒有明確的概念 檔案系統使用的基礎儲存空間:檔案系統可能需要存取權 封鎖裝置、網路或記憶體來儲存資料,但 VFS 層 只處理處理路徑、資料的位元組陣列和 V 節點的介面。

路徑步行

為開啟伺服器端資源,系統會提供伺服器一些起點 (由呼叫的控制代碼表示) 和字串路徑。這個路徑會分割成 「/」字元來「查詢」每個元件 提供給基礎檔案系統的回呼。如果查詢成功傳回 並偵測到另一個「/」區段,然後程序會繼續進行,直到 (1) lookup 找不到元件,(2) 路徑處理程序到達最後一個部分 或 (3) lookup 會找出「掛接元件」, 具有附加「remote」控制代碼的 vnode。目前我們會忽略掛接點 但是在檔案系統的相關章節中討論過節點 掛接功能

假設 lookup 已成功找到「foo」Vnode。檔案系統伺服器 將繼續呼叫 VFS 介面「開啟」, 您可以先使用提供的旗標存取資源,再呼叫「GetHandles」 詢問基礎檔案系統是否還需要其他控制代碼 與 Vnode 互動假設客戶要求 "foo" 物件 同步 (在預設的 POSIX 公開呼叫中即為隱含) 與「foo」互動所需的額外控制代碼已封裝至小型 FIDL 中 說明物件並傳回給用戶端。或是「foo」擁有 無法開啟,系統仍會傳回 FIDL 說明物件,但帶有 將「status」欄位設為錯誤代碼,表示失敗。假設 都能成功開啟檔案伺服器會繼續建立「iostate」物件 「foo」並向調度工具註冊。如此一來,日後對「foo」發出的呼叫 完全由伺服器處理「Foo」已開啟,客戶現在可以 再提出要求

就客戶而言,在「開放式」呼叫開始時, CWD 控制代碼已傳輸至遠端檔案系統。 伺服器由於呼叫是同步的,因此用戶端繼續等候 回應。正確找到伺服器、開啟並初始化後 這個檔案的 I/O 狀態,傳回了「成功」的 FIDL 說明物件。這個 物件便會被用戶端讀取,表示呼叫已完成 此時,用戶端可以建立 fdio 物件 代表「foo」的控制代碼,以檔案中的項目參照 然後將 Fd 傳回給命名為原始「開放」的人員 函式。此外,如果客戶想傳送其他請求 (例如「讀」或「寫」) 到「foo」,就可以直接與 下載至檔案系統伺服器 日後的要求,都必須透過「CWD」轉送

開放式的生命週期:圖表

+----------------+
| Client Program |
+----------------+
|   fd: x    |   fd: y    |
| Fdio (FIDL)| Fdio (FIDL)|
+-------------------------+
| '/' Handle | CWD Handle |
+-------------------------+
      ^            ^
      |            |
Zircon Channels, speaking FIDL                   State BEFORE open(‘foo’)
      |            |
      v            v
+-------------------------+
| '/' Handle | CWD Handle |
+-------------------------+
|  I/O State |  I/O State |
+-------------------------+
|   Vnode A  |   Vnode B  |
+-------------------------+
| Filesystem Server |
+-------------------+


+----------------+
| Client Program |
+-------------------------+
|   fd: x    |   fd: y    |
| Fdio (FIDL)| Fdio (FIDL)|
+-------------------------+
| '/' Handle | CWD Handle |   **foo Handle x2**
+-------------------------+
      ^            ^
      |            |
Zircon Channels, speaking FIDL                   Client Creates Channel
      |            |
      v            v
+-------------------------+
| '/' Handle | CWD Handle |
+-------------------------+
|  I/O State |  I/O State |
+-------------------------+
|   Vnode A  |   Vnode B  |
+-------------------------+
| Filesystem Server |
+-------------------+


+----------------+
| Client Program |
+-------------------------+
|   fd: x    |   fd: y    |
| Fdio (FIDL)| Fdio (FIDL)|
+-------------------------+--------------+
| '/' Handle | CWD Handle | ‘foo’ Handle |
+-------------------------+--------------+
      ^            ^
      |            |
Zircon Channels, speaking FIDL                  Client Sends FIDL message to Server
      |            |                            Message includes a ‘foo’ handle
      v            v                            (and waits for response)
+-------------------------+
| '/' Handle | CWD Handle |
+-------------------------+
|  I/O State |  I/O State |
+-------------------------+
|   Vnode A  |   Vnode B  |
+-------------------------+
| Filesystem Server |
+-------------------+


+----------------+
| Client Program |
+-------------------------+
|   fd: x    |   fd: y    |
| Fdio (FIDL)| Fdio (FIDL)|
+-------------------------+--------------+
| '/' Handle | CWD Handle | ‘foo’ Handle |
+-------------------------+--------------+
      ^            ^
      |            |
Zircon Channels, speaking FIDL                  Server dispatches message to I/O State,
      |            |                            Interprets as ‘open’
      v            v                            Finds or Creates ‘foo’
+-------------------------+
| '/' Handle | CWD Handle |
+-------------------------+
|  I/O State |  I/O State |
+-------------------------+-------------+
|   Vnode A  |   Vnode B  |   Vnode C   |
+------------------------------+--------+
| Filesystem Server |
+-------------------+


+----------------+
| Client Program |
+-------------------------+
|   fd: x    |   fd: y    |
| Fdio (FIDL)| Fdio (FIDL)|
+-------------------------+--------------+
| '/' Handle | CWD Handle | ‘foo’ Handle |
+-------------------------+--------------+
      ^            ^          ^
      |            |          |
Zircon Channels, FIDL         |                   Server allocates I/O state for Vnode
      |            |          |                   Responds to client-provided handle
      v            v          v
+-------------------------+--------------+
| '/' Handle | CWD Handle | ‘foo’ Handle |
+-------------------------+--------------+
|  I/O State |  I/O State |  I/O State   |
+-------------------------+--------------+
|   Vnode A  |   Vnode B  |    Vnode C   |
+------------------------------+---------+
| Filesystem Server |
+-------------------+


+----------------+
| Client Program |
+-----------------------------+----------+
|   fd: x    |   fd: y    |    fd: z     |
| Fdio (FIDL)| Fdio (FIDL)|  Fdio (FIDL) |
+-------------------------+--------------+
| '/' Handle | CWD Handle | ‘foo’ Handle |
+-------------------------+--------------+
      ^            ^           ^
      |            |           |
Zircon Channels, speaking FIDL |                  Client recognizes that ‘foo’ was opened
      |            |           |                  Allocated Fdio + fd, ‘open’ succeeds.
      v            v           v
+-------------------------+--------------+
| '/' Handle | CWD Handle | ‘foo’ Handle |
+-------------------------+--------------+
|  I/O State |  I/O State |  I/O State   |
+-------------------------+--------------+
|   Vnode A  |   Vnode B  |    Vnode C   |
+------------------------------+---------+
| Filesystem Server |
+-------------------+