FIDL 總覽

本文件將概略介紹 Fuchsia 介面定義 語言 (FIDL),也就是描述交錯流程的語言 Fuchsia 執行程式所使用的通訊 (IPC) 通訊協定。這篇簡介 介紹 FIDL 背後的概念,也就是開發人員熟悉這些概念 您可以按照教學課程開始編寫程式碼,或者 閱讀語言,即可深入探索 bindings 參照。

什麼是 FIDL?

FIDL」時代表「Fuchsia Interface Definition Language」字詞 也能用於指出多種不同概念:

  • FIDL 線格式:FIDL 電匯格式會指定 FIDL 的方式 訊息會以 IPC 傳輸為記憶體中的訊息
  • FIDL 語言:FIDL 語言是 .fidl 檔案中會說明通訊協定
  • FIDL 編譯器:FIDL 編譯器會產生用於 使用及實作通訊協定的計畫
  • FIDL 繫結:FIDL 繫結是 提供特定語言的執行階段支援程式庫和程式碼產生器 用於操縱 FIDL 資料結構和通訊協定的 API。

FIDL 的主要工作是讓各種用戶端和服務能夠互通。 將 IPC 機制的實施分離,有助於客戶多元性 而且是由自動產生程式碼簡化而成。

FIDL 語言提供熟悉 (但經過簡化) 的 C 類宣告 語法,讓服務供應商明確定義自己的通訊協定。基本版 資料類型 (例如整數、浮點數和字串) 可整理為 複雜的匯總結構和聯集固定陣列和動態大小 您可以透過基本類型和匯總型別建構向量 這些物件可以結合成更複雜的資料結構

由於用戶端實作目標語言的數量過多 (C、C++、Rust、Dart、Dart、 等等),我們不想要為這項服務的開發人員 因此建議您為每個應用程式建立通訊協定實作

此時 FIDL 工具鍊就派上用場了。服務的開發人員 只定義一個通訊協定的 .fidl 定義檔案。透過這個檔案 接著,FIDL 編譯器會以任何支援的 目標語言。

圖:以 C++ 編寫的伺服器與使用多個
語言

在許多情況下,伺服器只會實作一種 (例如, 該特定服務可能會在 C++ 中實作),但有可能 用戶端的實作數量,以及多種語言版本。

請注意,Fchsia 作業系統並不對 FIDL 有基本的認識。 FIDL 繫結採用 Fuchsia 的標準管道通訊機制。 FIDL 繫結和程式庫會強制執行一組語意行為和持續性 顯示頻道使用方式

FIDL 架構

從開發人員的觀點來看,主要元件如下:

  • FIDL 定義檔案:這是文字檔 (結尾是 .fidl, 慣例) 定義值以及通訊協定 (方法包括 參數),
  • 用戶端程式碼 - 由 FIDL 編譯器 (fidlc) 工具鍊產生的 每個特定譯文語言
  • 伺服器程式碼 — 也由 FIDL 編譯器工具鍊產生。

這裡是非常簡單的 FIDL 定義檔案範例,請考慮使用「echo」服務 — 無論是用戶端傳送至伺服器的任何內容,伺服器都會回應 用戶端。

為求清楚起見,我們加入了行號,此號碼不屬於 .fidl 檔案。

1   library fidl.examples.echo;
2
3   @discoverable
4   protocol Echo {
5       EchoString(struct {
6           value string:optional;
7       }) -> (struct {
8           response string:optional;
9       });
10  };

讓我們逐行看看。

第 1 行:library 關鍵字可用來定義 因此效能相當卓越不同程式庫中的 FIDL 通訊協定的名稱可能相同,因此 命名空間來區分兩者

第 3 行:@discoverable 屬性指明 確保用戶端都能連線使用下列通訊協定。

第 4 行:protocol 關鍵字會介紹通訊協定的名稱,此處為 名稱為 Echo

第 5 到 9 行:方法、其參數和傳回值。有兩個不尋常的 這條行的各方面:

  • 請注意宣告 string:optional (同時適用於 valueresponse)。 string 部分代表參數為字串 ( 字元),而 optional 限制則代表參數為 選用。
  • 參數會納入 struct,而這是頂層類型 包含方法參數
  • -> 部分代表在方法後方顯示的傳回值 宣告內容與 C++ 或 Java 不同的是,方法可以傳回多個 輕鬆分配獎金

上述 FIDL 檔案接著,已宣告一個名為 Echo 的通訊協定,其中包含一個 稱為 EchoString 的方法,可接受可為空值的字串,並傳回可為空值的字串 字串。

上述的簡易範例僅使用一種資料類型,兩者皆使用 string 做為輸入內容 方法與輸出內容

可能的 FIDL 資料類型相當有彈性:

type MyRequest = struct {
    serial uint32;
    key string;
    options vector<uint32>;
};

上述程式碼會宣告名為 MyRequest 的結構,其中包含三個成員: 非帶正負號的 32 位元整數,名為 serial,名為 key 的字串,向量 options 無正負號 32 位元整數

訊息模型

為了瞭解 FIDL 的訊息,我們需要將內容分成 以及釐清某些定義

在最下方 (作業系統層) 是非同步 針對傳送者接收器

  • sender — 產生訊息的一方
  • receiver - 收到訊息的一方

傳送訊息是一項非封鎖性作業:寄件者會傳送訊息 即可繼續處理,無論接收方正在執行什麼工作。

接收器如果想要等待訊息,可以將其封鎖。

頂層會實作 FIDL 訊息,並使用底部 (非同步) 執行用於處理用戶端伺服器

  • client - 發出要求的一方 (伺服器)。
  • server - 處理要求的一方 (代表 用戶端)。

「寄件者」一詞和「接收器」這點很合理 — 基本通訊機制並非 關心自己指派給各方的角色,只是因為 並開始接收。

「客戶」一詞和「server」以便討論這些角色扮演的角色 交響樂句具體來說,客戶可能是一次寄件者、 不同時間點的接收器;相同。

實務上,在用戶端 / 伺服器互動的情況下, 意指有多種模型

  1. 封鎖通話 — 用戶端會傳送至伺服器,等待回覆
  2. fire and 找不到 — 用戶端會傳送到伺服器,但沒預期回覆
  3. 回呼非同步呼叫:用戶端會傳送至伺服器,但不會 block;稍後再以非同步方式傳送回覆
  4. event - 伺服器會傳送至用戶端,但不要求用戶端 資料

第一個是同步,其餘則是非同步。之後會詳細說明 順序。

用戶端傳送至伺服器,等待回應

這種模式就是傳統的「封鎖呼叫」或「函式呼叫」支援語言 大部分的管道設計語言除外 因此可能會因為傳輸層級錯誤而失敗

從用戶端的角度來看,是由封鎖的呼叫組成。 伺服器會執行一些處理

圖:用戶端和伺服器

以下提供逐步說明:

  1. 用戶端發出呼叫 (可選擇包含資料) 和封鎖。
  2. 伺服器接收到用戶端的呼叫 (和選用的資料),並執行 所需的處理時間
  3. 伺服器可自行斟酌是否要回應用戶端,並提供選用資料。
  4. 伺服器的回覆會導致用戶端解除封鎖。

為了以非同步訊息傳遞這個同步訊息模型 配置很簡單。提醒您,用戶端對伺服器和伺服器對用戶端 訊息傳輸是位於通訊協定底部的一層,並屬於非同步性質。 系統就會在用戶端進行同步時,讓用戶端封鎖到 伺服器的郵件送達。

基本上,在這個模型中,用戶端和伺服器已達成協議:

  • 資料流程是由用戶端啟動
  • 客戶最多只能有一則尚未解決的訊息
  • 伺服器只會在回應客戶的 訊息
  • 用戶端應等候伺服器回應再繼續。

此封鎖模式經常是在客戶需要回覆時 然後再繼續傳送要求

舉例來說,用戶端可能會向伺服器要求資料,但無法執行這項操作 直到資料送達為止。

或者,客戶可能需要按特定順序執行步驟, 確認每個步驟都完成之後,再啟動下一個步驟。如果發生錯誤 發生這類情況時,用戶端可能需要執行適度修正動作 作業已完成 — 其他要同步至 每個步驟的完成時間

用戶端傳送至伺服器,沒有回覆

此模型也稱為「射後不理」。在範例中,用戶端會將 訊息傳送至伺服器然後繼續執行作業對比 封鎖模型,用戶端「不會」封鎖也不會預期 回應

當用戶端不需要 (或無法) 不需要時,就可以使用這個模型 和處理其要求的過程。

圖:火災與遺忘;用戶端會意外傳送至伺服器
回覆

典型的例子就是記錄系統。用戶端將記錄資訊傳送至 記錄伺服器 (在上圖中圈出「1」和「2」),但沒有原因 再決定是否封鎖伺服器端可能出現許多問題:

  1. 伺服器忙碌中,目前無法處理寫入要求,
  2. 媒體空間已滿,伺服器無法寫入資料
  3. 伺服器發生錯誤
  4. 依此類推

不過,客戶沒有辦法處理這些問題,因此 封鎖功能只會產生更多問題

用戶端傳送至伺服器,但不會封鎖

以及下一個模型 (「伺服器會傳送至用戶端,用戶端未詢問 資料」) 相似。

在目前的模式中,用戶端會將訊息傳送至伺服器,但不會封鎖。 不過,用戶端會預期伺服器發出某種回應,但金鑰 這表示它並未與請求同步

因此用戶端 / 伺服器互動體驗非常靈活。

同步模型會強制用戶端等候伺服器回應, 目前的模型可讓用戶端在伺服器 處理要求:

圖:用戶端傳送至伺服器,但直到稍後才封鎖

此圖表與上方類似中的細微差異 「1」社交圈用戶端仍在執行。客戶決定要放棄的時機 CPU;也不會與訊息同步

實際上有兩個子類別 — 另一種情況則是用戶端 一個回應,另一個則用戶端可以收到多個回應。( 其中一個用戶端接收零回應,就是「射後不理」 先前提過)。

單一要求、單一回應

單一回應案例最接近同步模型:用戶端 傳送訊息,最終通過伺服器回覆。這個模型 而非多執行緒。舉例來說,如果您知道用戶端可能會 仍在等待伺服器回應時執行實用工作。

單一請求、多個回應

多重回應案例可用於「訂閱」模型客戶的 「primes」訊息例如要求存取 可能會發生什麼事

接著,客戶只看著自己的業務。

一段時間後,伺服器發現用戶端 便成為客戶,進而傳送訊息給用戶端客戶來源: 這類訊息是「回覆」,用戶端接收到 以非同步方式套用至其要求

圖:用戶端傳送至伺服器,伺服器回覆多個
次

伺服器無法傳送其他 觸發事件;即為「多次回應」模型版本 請注意,第二項 (及後續) 回應「不會在」用戶端傳送 傳送任何額外訊息

請注意,用戶端不需要等待伺服器傳送 撰寫新的電子郵件訊息在上圖中,我們顯示用戶端處於封鎖狀態 在圓圈「3」之前— 用戶端也可能一直在執行中。

伺服器將伺服器傳送至用戶端,但不要求客戶提供資料

這個模型也稱為「事件」模型

圖:從伺服器傳至用戶端的垃圾郵件

在其中,用戶端準備接收伺服器的訊息,但不知情 訊息的預期效果 — 訊息不僅非同步到 用戶端也包含 (從用戶端 / 伺服器視角) 「未經請求」 表示用戶端未明確提出要求 (就像上一個 模型,如上所示)。

用戶端會指定要呼叫的函式 (「事件處理函式」) 但伺服器傳送的郵件仍與伺服器有關 的商機。

由伺服器自行斟酌 (上圖中的「1」和「2」圈) 時,系統將顯示訊息 是以非同步方式傳送至用戶端,並由用戶端指定的 函式。

請注意,用戶端在傳送訊息時可能已開始運作 (如圓圈中所示) 「1」),或者用戶端什麼都不做,而且正在等待訊息 (如圓形「2」)。

用戶端不必等待訊息。

非同步訊息傳遞複雜度

將非同步訊息拆分到上列 (或任意選擇) 主要是用來呈現一般的使用模式 完整詳盡。

在最常見的非同步訊息傳遞的情況下,用戶端為零或多個用戶端 郵件與零或多個伺服器回覆的連結鬆散。就是「寬鬆 關聯」這會增加設計程序的複雜性。

FIDL 中的處理序間通訊 (IPC) 模型

現在我們已瞭解 IPC 模型,以及模型與模型的互動方式 FIDL 的非同步訊息傳遞 我們來看看如何定義這些訊息

我們會將其他模式 (觸發和忘記,以及非同步呼叫或事件) 新增至通訊協定 定義檔:

1   library fidl.examples.echo;
2
3   @discoverable
4   protocol Echo {
5       EchoString(struct {
6           value string:optional;
7       }) -> (struct {
8           response string:optional;
9       });
10
11      SendString(struct { value string:optional; });
12
13      ->ReceiveString(struct { response string:optional; });
14  };

第 5-9 行是上述的 EchoString 方法,屬於 傳統函式呼叫訊息,用戶端會呼叫 EchoString, (選用) 字串,然後封鎖,等待伺服器回應另一個 選用字串。

Line 11SendString 方法。此函式沒有 -> 回傳 宣告,導致部分原因「後來被忽視」模型 (僅限傳送)、 因為我們已經告知 FIDL 編譯器,這個特定方法沒有 相關的值。

請注意,它並不屬於傳回參數,而是缺少 傳回宣告的金鑰,並在其中輸入「-> ()」晚於 SendString 會改變「射後不理」樣式的意義 方法,用來宣告沒有任何回傳的函式呼叫樣式方法 引數。

Line 13ReceiveString 方法。它稍有不同 第一部分沒有方法名稱,而是在 -> 運算子。這會告知 FIDL 編譯器這是「非同步呼叫」或 「活動」宣告方式

FIDL 繫結

FIDL 工具鍊採用 FIDL 通訊協定及類型定義,例如 範例,然後使用各種譯文語言產生可「朗讀」的程式碼 這些通訊協定產生的程式碼稱為 FIDL 繫結 視語言而定,有多種口味可供選擇:

  • 原生繫結:針對高度敏感的情境 (例如裝置) 驅動程式和高處理量伺服器 利用就地存取功能,避免記憶體 但可能需要更瞭解 開發人員內建的通訊協定
  • 慣用繫結:藉由複製這些繫結,打造更適合開發人員的體驗 轉換為更方便使用的資料類型 (例如採用堆積後的資料) 但相對來說,效率較低 結果。

繫結提供多種叫用通訊協定方法的方式,具體取決於 語言:

  • 傳送/接收:直接讀取或撰寫至管道訊息,非內建功能 等待迴圈 (C)
  • 根據回呼:系統會以非同步方式傳送接收的訊息, 在事件迴圈 (C++、Dart) 上回呼
  • 通訊埠式:接收的訊息會傳送至通訊埠或未來 (Rust)
  • 同步呼叫:等待回覆並傳回 (Go、C++ 單元測試)

繫結可提供下列部分或所有主體作業:

  • 編碼:就地將原生資料結構轉換為線路格式 (與驗證搭配使用)
  • 解碼:就地將線格式資料轉換為原生資料結構 (與驗證搭配使用)
  • 複製/移至慣用表單:複製原生資料結構的內容 轉換成慣用的資料結構 並將帳號代碼移至新系統
  • 複製/移至原生表單:複製慣用資料結構的內容 轉換為原生資料結構中的帳號代碼後
  • Clone:複製原生或慣用的資料結構 (不含 僅限移動的類型)
  • Call (呼叫):叫用通訊協定方法

用戶端實作

無論目標語言為何,fidlc FIDL 編譯器都會產生用戶端 其基本結構如下

第一部分包含管理和背景處理 包含:

  1. 提供了與伺服器連線的方法
  2. 已開始非同步 (「背景」) 訊息處理迴圈
  3. 非同步呼叫樣式和事件樣式方法 (如有) 會與訊息繫結 循環播放

第二部分包含傳統函式呼叫的實作: 觸發及忘記樣式方法。 一般而言,這包括:

  1. 建立可呼叫的 API 和宣告
  2. 為每個 API 產生程式碼,以將呼叫的資料彙整至 FIDL 經過格式化的緩衝區,適合傳輸至伺服器
  3. 產生程式碼,以將資料傳送至伺服器
  4. 屬於函式呼叫樣式呼叫時,會產生以下程式碼:
    1. 等待伺服器回應
    2. 解開 FIDL 格式緩衝區的資料,以及
    3. 透過 API 函式傳回資料。

確切步驟可能會因語言執行差異而異 但這只是基本的描繪

伺服器實作

fidlc FIDL 編譯器也會針對特定目標產生伺服器程式碼 語言。和用戶端程式碼一樣,這段程式碼的結構一致。 目標語言。程式碼:

  1. 讓用戶端可以連線的物件
  2. 並啟動主要處理迴圈:
    1. 等候訊息
    2. 呼叫實作函式以處理訊息
    3. 如果有指定,系統會發出非同步呼叫給用戶端,以傳回 輸出

在後續章節中,我們會詳細說明每種語言的實作方式 用戶端和伺服器程式碼

為什麼要使用 FIDL?

Fuchsia 廣泛仰賴處理序間通訊 (IPC),因為大部分功能都是在 核心外的使用者空間,包括裝置等特殊權限元件 驅動程式。因此 IPC 機制必須有效、確定性 功能完善且易於使用:

IPC 效率是指產生 傳輸及耗用不同程序之間的訊息處理序間通訊 (IPC) 因此必須高效率FIDL 編譯器必須 不會產生過度間接或隱藏成本,而產生緊密的程式碼。其應該是 至少在手動編寫程式碼的情況下才是最重要的位置

IPC 確定性是指在 已知的資源信封。IPC 將由關鍵系統廣泛使用 服務,例如檔案系統服務 為許多用戶端提供服務,且必須於 且可預測的方式FIDL 線格式必須提供嚴格的靜態保證,例如 因為能確保結構大小和版面配置是可變動的,從而減輕 就需要動態記憶體分配或複雜的驗證規則

IPC 穩定性涉及將處理序間通訊 (IPC) 視為最重要的部分 作業系統的 ABI維持二進位檔的穩定性至關重要,機制 通訊協定演變的設計必須謹慎設計,以免違反 現有服務及其客戶的不一致,尤其是在 此外,也會考慮FIDL 繫結必須有效執行 進行輕量、嚴格驗證

IPC 易用性涉及 IPC 通訊協定是不可或缺的 作業系統 API 的一部分提供優質開發人員至關重要 透過處理序間通訊 (IPC) 存取服務的行為效率。FIDL 程式碼產生器會移除 手動編寫 IPC 繫結的負擔此外,FIDL 程式碼產生器 並依照不同目標對象的需求,產生各種繫結 慣用語。

目標

FIDL 是針對上述特性進行最佳化設計。於 特別是 FIDL 的設計旨在滿足下列目標:

優先權

  • 說明 Zircon 上 IPC 使用的資料結構和通訊協定。
  • 已針對處理序間通訊最佳化。雖然 FIDL 也用於 而用於磁碟傳輸和網路傳輸時,其設計並未針對 這些次要用途
  • 有效率地傳輸訊息,內容包括資料 (位元組) 和功能 (處理常式)。
  • 經過特別設計,可有效利用 Zircon 基本功能。 雖然其他平台採用 FIDL (例如透過 ffx),但其設計 先從 Fuchsia 優先。
  • 提供便利的 API,可用於建立、傳送、接收和使用 訊息。
  • 執行充分驗證,確保通訊協定不變 (但不再這麼做) 而非其他內容)。

睡眠效率

  • 速度和記憶體容量一樣,就像使用手捲式資料結構一樣 答案是:
  • 有線格式使用未經壓縮的原生資料類型,有些則微弱, 正確對齊,確保能直接存取郵件內容。
  • 無須動態分配記憶體,即可產生或使用訊息 或者大小是靜態資訊或受限時
  • 使用純移動語意明確處理擁有權。
  • 資料結構封裝順序是標準化且清楚明確,且至少達到最低要求 邊框間距。
  • 避免使用後修補指標。
  • 請避免執行費用高昂的驗證作業。
  • 請避免計算會溢位的計算。
  • 利用通訊協定要求管道進行非同步作業。
  • 結構是固定的大小;而即時儲存的資料一律為非線上儲存。
  • 結構並非自述;FIDL 檔案會描述其內容。
  • 沒有結構的版本,但可使用新方法擴充通訊協定 。

人體工學

  • 由 Fuchsia 團隊維護的程式設計語言繫結:
    • C、新 C++、高階 C++ (舊版)、Dart、Go、Rust
  • 請注意,我們日後可能會支援其他語言,例如 為:
    • Java、JavaScript 等
  • 繫結和產生的程式碼有原生或慣用的變種版本 會因應用程式而異
  • 使用編譯時間程式碼產生功能,將訊息序列化最佳化。 反向序列化與驗證
  • 使用容易存取且程式設計語言 FIDL 語法的程式設計語言 不會有錯
  • FIDL 提供程式庫系統,可簡化其他機構的部署和使用程序 開發人員。
  • FIDL 表示系統 API 所需的最常用資料類型。它會 不是希望為所有提供的類型提供一對一的全方位對應 可使用 Vertex AI Pipelines SDK

工作流程

本節總結 IPC 作者、發布者與消費者的工作流程 使用 FIDL 描述的通訊協定。

正在編寫 FIDL

若是以 FIDL 為基礎的通訊協定,作者會建立一或多個 *.fidl 檔案, 說明自己的資料結構、通訊協定和方法。

FIDL 檔案依照作者編入一或多個 FIDL 程式庫。每項 程式庫代表一組邏輯相關的功能, 程式庫名稱。位於同一程式庫中的 FIDL 檔案隱含存取所有 同一個程式庫中的其他宣告。宣告的順序 組成程式庫的 FIDL 檔案並不重要。

每個程式庫的 FIDL 檔案都可以藉由以下方式存取另一個 FIDL 程式庫中的宣告: 匯入其他 FIDL 模組。匯入其他 FIDL 程式庫後 可用於建構衍生通訊協定 。匯入的符號必須使用程式庫名稱或別名進行認證 可以防止命名空間衝突

正在發布 FIDL

以 FIDL 為基礎的通訊協定的發行商負責建立 FIDL 程式庫 供消費者使用例如,作者可以發布 FIDL 程式庫 或以 SDK 形式發布。

消費者只需要將 FIDL 編譯器指向包含 程式庫 (及其依附元件) 的 FIDL 檔案,為該程式庫產生程式碼 資源庫。具體做法則由我們代為處理 包括建構系統

使用 FIDL

以 FIDL 為基礎的通訊協定的消費者會使用 FIDL 編譯器產生程式碼 適合與其語言執行階段專屬繫結搭配使用。某些 消費者可能會擁有幾種不同的變種版本 這些產生的程式碼都能在電線格式層級互通,但 而不是來源層級

在 Fuchsia 世界建構環境中,透過 FIDL 程式庫產生程式碼 個別 FIDL 版本會自動為所有相關語言處理 每個程式庫的目標

在 Fuchsia SDK 環境中,系統會透過 FIDL 程式庫產生程式碼 做為編譯應用程式的一部分

開始使用

如要進一步瞭解如何使用 FIDL,請參閱「指南」部分 開發人員指南教學課程: 。如果您在 Fuchsia 進行開發,想進一步瞭解 如現有 FIDL API 使用繫結,請參閱 FIDL 繫結 參考資料。最後,如果您想進一步瞭解 FIDL 或 請看看 FIDL 的語言 參考資料著作文件