| RFC-0235:元件字典 | |
|---|---|
| 狀態 | 已接受 |
| 區域 |
|
| 說明 | 提案:在元件架構中導入字典類型,以進行能力組合。 |
| 問題 | |
| Gerrit 變更 | |
| 作者 | |
| 審查人員 | |
| 提交日期 (年-月-日) | 2023-10-16 |
摘要
這項 RFC 建議使用執行階段和宣告式 API,建立及傳送功能組合 (稱為「字典」)。
提振精神
目前,元件架構僅支援其定義的功能「點對點」路徑:如要將能力 C 從元件 A 傳送至元件 B,每個中繼元件都必須有路徑區隔,才能在相鄰元件之間傳送 C。
許多用途都可望受益於將一組功能做為單一邏輯單元進行路徑設定的能力。如果沒有這項功能,這些客戶就必須採用成本高昂、缺乏彈性且容易出錯的變通方法。以下是這些用途的範例:
- 幾乎每個元件都會使用
LogSink、InspectSink和追蹤供應器等診斷功能,如果能將這些功能做為套件進行路由,就能大幅簡化路由拓撲。 - 剖析器功能 (例如
fuchsia.debugdata.Publisher) 與診斷功能屬於類似類別,但只會在特定建構版本中啟用。目前,我們還沒有好的方法,可讓建構作業設定需要涵蓋整個拓撲的能力。因此,分析器目前僅在測試領域中啟用。 - 測試領域 Proxy 會向測試案例公開介面,測試案例會使用該介面來執行受測元件。目前這是自訂介面,與元件連線 API 非常相似。這個介面可以替換成一組功能,由測試領域 Proxy 轉移至測試。這樣一來,測試領域 Proxy 就不必定義自己的抽象層,而且可以讓測試使用任何元件架構功能。
- 如果 Anywhere 功能是透過多個層級轉送,則可透過套裝組合簡化轉送程序:
session_manager:所有從core轉送至session_manager的功能,都需要由session_manager.cml重新轉送。這是一大組功能,因此難以維護,而且表示部分非平台功能會洩漏到session_manager.cml中。chromium:Chromium CML 檔案包含許多重複項目,如果可以將功能分組並以單一名稱進行路徑設定,就能大幅簡化檔案。https://fxbug.dev/42072339)
- 元件架構環境是一項功能,可設定執行器和解析器,供整個子樹狀結構使用,並隱含地進行路由。如果我們能改用通用的組合 API 來完成這項作業,就能與其餘的元件架構路徑 API 更加協調一致,並減輕環境式隱含路徑的最低權限疑慮。
以下也是能力組合的動機使用案例,但這些案例有一些特殊考量,需要後續設計工作,不屬於本 RFC 中的提案。
- 部分主機板專屬驅動程式會公開平台未定義的自訂服務。這些服務應由
bootstrap領域公開,因為所有驅動程式庫元件都位於該領域,但在平台拓撲中明確命名這些元件並不合理。如果 CML 能將這些服務組合在一起,我們就能解決這個問題。 - 使用
driver_test_realm的測試也會發生類似問題。這些測試希望將驅動程式庫中的不同服務路徑導向測試。在這些測試中,驅動程式庫測試領域元件位於驅動程式和測試之間,我們希望能夠在這些測試中重複使用驅動程式庫測試領域,而不需修改。
最後,現有幾個元件架構 API 已經包含分組功能。不過,這些規則是獨立的,只適用於特定情況。以下是一些例子:
- 命名空間是所有路由至程式的功能 (位於
use宣告中) 的分組。 - 「公開目錄」是元件路由至父項的功能分組,換句話說,就是元件的公開介面。
- 服務功能是將通訊協定分組的方式,服務功能本身也可以分組,形成「匯總」服務功能。
我們有這麼多 API,表示使用者可從一般抽象化中獲益,定義自己的字典並將其路徑傳送出去。
利害關係人
根據上述用途,我們已確認下列團隊為利害關係人:
- 建築
- 診斷
- 測試
- 工具鏈
- 驅動程式架構
- 安全性
導師:hjfreyer@
審查者:
- abarth@ (Architecture)
- crjohns@ (測試)
- markdittmer@ (安全性)
- miguelfrde@ (診斷)
- surajmalhotra@ (驅動程式)
- ypomortsev@ (元件架構)
已諮詢:
- phosek@
- kjharland@
- anmittal@
- wittrock@
- novinc@
社交:
這份 RFC 之前有兩份內部文件:用途和需求文件,以及核心設計文件。這些文件已獲得利害關係人的非正式核准。
本 RFC 已納入這些文件中的相關資訊。
需求條件
這些是作業字典必須支援的項目,我們透過分析用途並從現有分組 API 概括而得:
- 第一類:字典是 CF API 中的第一類概念,應以能力表示。
- 匯總:有一個「匯總」作業可從一組功能建構字典。
- 擷取:有一項「擷取」作業可從字典擷取個別能力,並像任何能力一樣進行路徑設定和使用。這大致上與匯總相反。
- 委派:字典可在元件之間傳遞。
- 巢狀結構:由於字典是一種能力,因此字典可以包含其他字典。
- 結構:字典會標記中繼資料,精確指出所含功能。
- 擴充功能:有一項作業可建構新的字典
B',該字典會繼承B的內容,並新增其他功能。 - 可變動性:字典內容可能會隨時間變更。不過,可能存在較高層級的政策,限制特定字典的可變動性。
設計
定義
「字典」定義為鍵/值組合的集合,其中鍵是「功能名稱」 (例如 fuchsia.example.Echo),值為 CF 能力。
功能名稱是來自 [A-Za-z0-9_-.] 集合的一或多個字元序列,大小為 1 到 N (目前 N = 100,但我們可能會在日後擴充)。
執行階段的字典
我們將推出公開的 FIDL 通訊協定,提供字典介面。以 FIDL 虛擬程式碼表示:
library fuchsia.component;
type DictionaryEntry = resource struct {
key DictionaryKey;
value Capability;
};
protocol Dictionary {
Insert(DictionaryEntry) -> ();
Remove(DictionaryKey) -> (Capability);
Lookup(DictionaryKey) -> (Capability);
Enumerate() -> Iterator<DictionaryKey>;
Clone();
};
我們也會推出可公開探索的 FIDL 通訊協定,讓呼叫端建立空白字典。
後續設計工作會決定 Capability 的精確型別定義。
元件聲明中的字典
首先,我們將稍微正式地定義作業,我們會定義與每個元件相關聯的四個特殊字典:元件輸入字典、元件輸出字典、程式輸入字典和程式輸出字典。這些是根字典。
元件輸入字典是包含父項傳遞給元件的所有功能 offer 的字典;換句話說,元件可以路由傳送的所有功能 from parent。
元件輸出字典是包含元件向父項提供的所有功能 expose 的字典;換句話說,父項可以將所有功能 from #component 傳送至元件。
程式輸入字典:字典,內含元件的所有功能。use
程式輸出字典是包含元件所有 capability 宣告的字典。
我們可以根據這些定義,表示能力轉送作業:
use、offer、expose和capabilities是路由作業,可在字典之間路由功能。use會將根字典中的能力轉送至程式輸入字典。expose會將能力從根字典路由至元件輸出字典。offer會將能力從根字典路由至子項的元件輸入字典。capabilities會在程式輸出字典中提供可供轉送的能力。
有了這項設計,我們就能將其一般化,允許轉送作業使用任意字典做為來源,而不僅限於根字典:
use會將能力從字典路徑傳送至程式輸入字典。expose會將字典中的能力,傳送至元件輸出字典。offer會將能力從字典路徑傳送至子項的元件輸入字典 或其他字典。
宣告
如要定義全新的空白字典,請執行下列操作:
capabilities: [
{
dictionary: "diagnostics-bundle",
},
],
如要定義內容繼承自現有字典的字典,請使用 extends 提供該字典的路徑 (請參閱「擴充功能」):
{
dictionary: "diagnostics-bundle",
extends: "parent/logging-bundle/sys",
},
匯總
如要將功能匯總至字典,請使用 offer 中的 to 關鍵字,將功能路徑傳送至字典。由於字典是由這個元件定義,因此包含該字典的根字典為 self。所有相同的關鍵字都支援做為一般 offer。舉例來說,您可以使用 as 關鍵字,在目標字典中變更能力名稱。
capabilities: [
{
dictionary: "diagnostics-bundle",
},
],
offer: [
{
protocol: "fuchsia.logger.LogSink",
from: "#archivist",
to: "self/diagnostics-bundle",
},
{
protocol: "fuchsia.inspect.InspectSink",
from: "#archivist",
to: "self/diagnostics-bundle",
},
{
directory: "publisher",
from: "#debugdata",
to: "self/diagnostics-bundle",
rights: [ "r*" ],
as: "coverage",
},
],
委派
委派是指轉送字典:
offer: [
{
dictionary: "diagnostics-bundle",
from: "parent",
to: "#session",
},
],
與其他能力路徑一樣,您可以使用 as 關鍵字變更目標名稱。
offer: [
{
dictionary: "logging-bundle",
from: "parent",
to: "#session",
as: "diagnostics-bundle",
},
],
系統會提供適用於動態優惠的對等執行階段 API。
巢狀結構
字典可以包含其他字典,方法是使用彙整語法,將字典路徑導向另一個字典:
capabilities: [
{
dictionary: "session-bundle",
},
],
offer: [
{
dictionary: "driver-services-bundle",
from: "parent",
to: "self/session-bundle",
},
],
擷取
正式化後,我們會擴充 from 關鍵字,不僅接受根層級字典,也接受巢狀根層級字典。
如要從字典中擷取能力,請在 from 中命名字典。這個字典與根字典 (parent、#child 等) 相關。
offer: [
{
protocol: "fuchsia.ui.composition.Flatland",
from: "parent/session-bundle",
to: "#window_manager",
},
],
這也適用於 use:
use: [
{
protocol: "fuchsia.ui.composition.Flatland",
from: "parent/session-bundle",
},
],
如果字典巢狀內嵌於其他字典,也可以進行擷取:
use: [
{
protocol: "fuchsia.ui.composition.Flatland",
from: "parent/session-bundle/gfx",
},
],
擴充功能
在字典定義中使用 extends 選項,從其他字典繼承:
capabilities: [
{
dictionary: "session-bundle",
// `session-bundle` is initialized with the dictionary the parent
// offered to this component, also called `session-bundle`.
extends: "parent/session-bundle",
},
],
offer: [
{
dictionary: "session-bundle",
from: "self",
to: "#session-manager",
},
{
protocol: "fuchsia.ui.composition.Flatland",
from: "#ui",
to: "self/session-bundle",
},
],
可變動性
以宣告方式建構的字典是不可變動的,這是一項實用的安全性屬性。如要讓字典可變動,必須在執行階段建立字典。
字典中功能的中繼資料
將功能放入字典時,功能會保留所有型別資訊和中繼資料,與字典本身相關聯的任何中繼資料則會分開。
舉例來說,如果元件 A 將具有 optional 供應情形的能力新增至字典,而元件 B 擷取該能力,即使字典本身的供應情形為 required,該功能在擷取時仍會具有 optional 供應情形。
我們可能會在匯總時對供應情形設下某些限制。
舉例來說,禁止將 required 能力放入 optional 字典可能很合理,因為這會違反從目標到來源進行路徑設定時,可用性絕不會變弱的常見不變量。
執行階段和宣告式字典之間的互通性
在執行階段建立的字典必須與元件宣告中的字典互通。否則,使用者就只能選擇其中一個,這也表示套裝組合的概念基礎不夠一般化,無法以類似方式解決這兩種用途。
互通性設計的詳細資料將在後續提案中說明。
這項功能目前已知的主要用途是驅動程式庫架構,用於轉送在執行階段填入的服務套件。
實作
在 cml 中加入字典,會遵循導入新 cml 功能的正常程序。首先,我們會將字典新增至 cml 和 component.decl
結構定義。接著,我們會更新 cmc、cm_fidl_validator、cm_rust 和 realm_builder,以編譯、驗證及表示字典。Scrutiny 也會更新,以辨識字典並驗證字典路徑。
我們已著手將字典 (做為 Rust 型別) 整合至元件模型和路由引擎。字典 API 的實作應以這項工作為基礎,將這些字典做為公開字典 API 的後端,以及用於路由字典功能的傳輸方式。
效能
沒有特別的成效考量。字典的路由速度應與個別路由組成功能的速度相同或更快。
人體工學
改善建構元件拓撲的人體工學,是這項設計的主要動機。
雖然導入新功能自然會增加 API 的複雜度,但我們相信,在拓撲中納入字典後,複雜度降低的幅度會遠遠超過上述影響。
回溯相容性
cmc 目前不支援元件資訊清單功能的版本控管,因此請務必小心,避免破壞與預先建構資訊清單的相容性。幸好,字典導入的所有新語法都與舊語法相容,因此這項工作並不困難。舉例來說,from 中的目前名稱語法會成為新路徑語法的特殊情況。
如果能力路徑的一部分通過字典,與該能力相關的任何安全性政策仍須適用。
安全性考量
在字典中路由傳送功能時,由於字典內功能的 ID 會向路徑中的中繼元件隱藏,因此會失去部分透明度。不過,您還是可以反向追蹤路徑至供應商,這是為了實現字典帶來的彈性和強大功能而做出的妥協。
以宣告方式建構的字典不可變動。對於這些字典,您可以從目標到來源,對字典的匯總路徑執行深度優先搜尋,取得內容的完整說明。
如果字典取代環境,系統的安全防護機制就會提升,因為字典會明確地以與其他功能相同的方式進行路由,這與環境不同。
隱私權注意事項
這項提案不會影響隱私權。
測試
我們會像測試大多數元件管理工具功能一樣,在 component_manager 和 cmc 中進行單元測試,並在 component_manager/tests 中進行整合測試。我們也會在審查中加入整合測試,以練習套用至含有字典的路徑的字典和政策。
說明文件
我們會在 //tools/lib/cml 中更新 rustdoc。
在 //docs/concepts/components 中新增頁面,說明字典。
在 //examples/components 中新增範例。
缺點、替代方案和未知事項
方法 1:字典的 in 和 into 關鍵字
宣告
字典功能是 cml/component.decl 能力類型,可授予字典存取權。
您可像其他元件架構能力一樣宣告字典。字典建立方式有兩種,取決於是否有 extends 關鍵字。
首先,您可以定義全新的空白字典:
capabilities: [
{
dictionary: "diagnostics-bundle",
},
],
或者,您也可以在 extends 中指定字典路徑,並在 from 中指定來源,藉此定義字典,其內容會從現有字典繼承 (請參閱「擴充功能」):
{
dictionary: "diagnostics-bundle",
extends: "logging-bundle/sys",
from: "parent",
},
匯總
如要將功能匯總至字典,請使用 into 關鍵字將功能路徑傳送至字典:
capabilities: [
{
dictionary: "diagnostics-bundle",
},
],
offer: [
{
protocol: "fuchsia.logger.LogSink",
from: "#archivist",
into: "diagnostics-bundle",
},
{
protocol: "fuchsia.inspect.InspectSink",
from: "#archivist",
into: "diagnostics-bundle",
},
{
protocol: "fuchsia.debugdata.Publisher",
from: "#debugdata",
into: "diagnostics-bundle",
},
],
委派
與主要設計相同。
巢狀結構
只要使用彙整語法將字典路徑傳送至字典,即可讓字典包含其他字典:
capabilities: [
{
dictionary: "session-bundle",
},
],
offer: [
{
dictionary: "driver-services-bundle",
from: "parent",
into: "session-bundle",
},
],
擷取
我們推出新的 in 關鍵字,用於指定要從中擷取能力的字典。
如果存在 in,能力關鍵字 (例如 protocol) 是指這個字典中的能力,而不是 from 中具名元件直接提供的能力。
in 可以是名稱或路徑 (其中名稱可視為退化案例)。如果是名稱,則是指 from 提供的字典。如果是路徑,路徑的第一個區段會指定 from 呈現的字典,路徑的其餘部分則會指定巢狀字典的路徑。
in 支援 use、offer、expose。這項設定為選用。
offer: [
{
protocol: "fuchsia.ui.composition.Flatland",
from: "parent",
in: "session-bundle/gfx",
to: "#window_manager",
},
],
expose: [
{
protocol: "fuchsia.ui.composition.Flatland",
from: "#scenic",
in: "gfx-bundle",
},
],
use: [
{
protocol: "fuchsia.ui.composition.Flatland",
in: "session-bundle/gfx",
},
],
擴充功能
在字典定義中使用 extends 關鍵字,從其他字典繼承:
capabilities: [
{
dictionary: "diagnostics-bundle",
extends: "parent/logging-bundle/sys",
},
],
offer: [
{
dictionary: "diagnostics-bundle",
from: "self",
to: "#session-manager",
},
{
protocol: "fuchsia.tracing.provider.Registry",
from: "#trace_manager",
into: "diagnostics-bundle",
},
],
替代方案 2:能力 ID 中的路徑
字典路徑可以做為能力 ID (protocol、directory 等) 的一部分,而不必在 from 中指定字典路徑。
匯總
與主要設計相同。
委派
與主要設計相同。
巢狀結構
與主要設計相同。
擷取
如要從字典中擷取能力,請在能力 ID (protocol、directory 等) 中指定字典內能力的路徑。
offer: [
{
protocol: "session-bundle/fuchsia.ui.composition.Flatland",
from: "parent",
to: "#window_manager",
},
],
目標中的名稱預設為最後一個路徑元素或「dirname」(fuchsia.ui.composition.Flatland)。您也可以使用 as 重新命名:
offer: [
{
protocol: "session-bundle/fuchsia.ui.composition.Flatland",
from: "parent",
to: "#window_manager",
as: "fuchsia.ui.composition.Flatland-windows",
},
],
這也適用於 use,可讓程式使用字典中的能力。如同其他 use 宣告,預設目標路徑會根據 /svc 重新設定名稱 (最後一個路徑元素):
use: [
{
protocol: "session-bundle/fuchsia.ui.composition.Flatland",
path: "/svc/fuchsia.ui.composition.Flatland", // default
},
],
路徑語法也適用於巢狀字典:
use: [
{ protocol: "session-bundle/gfx/fuchsia.ui.composition.Flatland" },
],
擴充功能
在字典定義中使用 origin: #... 選項,從其他字典繼承:
capabilities: [
{
dictionary: "session-bundle",
// Source of the dictionary to extend (in this case, the one named
// "session-bundle" from the parent)
origin: "#session",
from: "parent",
},
],
offer: [
{
dictionary: "session-bundle",
from: "self",
to: "#session-manager",
},
{
protocol: "fuchsia.ui.composition.Flatland",
from: "#ui",
into: "session-bundle",
},
],
替代方案 3:功能 ID 成為路徑
名稱 -> 路徑
正式來說,cml 中的能力 ID 是名稱,沒有內建的巢狀結構。例如:
offer: [
{
protocol: "fuchsia.fonts.Provider",
from: "#font_provider",
to: "#session-manager",
},
],
不過,功能 ID 會對應至 capabilities 和 use 區段中的路徑。對於通訊協定,這通常是隱含的:如果未提供路徑,cmc 會填入 /svc/${capability-name} 的預設路徑。例如:
use: [
{
protocol: "fuchsia.fonts.Provider",
// path in namespace
path: "/svc/fuchsia.fonts.Provider",
},
],
capabilities: [
{
protocol: "fuchsia.fonts.Provider",
// path in outgoing directory
path: "/svc/fuchsia.fonts.Provider",
},
],
這個替代方案會能力中的路徑。更正式地來說:
- 功能 ID 是由字元集
[A-Za-z0-9_-.]中的一或多個名稱組成,長度介於 1 到 100 個字元之間,並以/字元分隔。不得使用前置/。- 或是規則運算式語法:
[A-Za-z0-9_-]{1,100}(/[A-Za-z0-9_-]{1,100})* - 現有的能力 ID 可與新語法相容。
- 或是規則運算式語法:
下文將說明這項語法如何自然地為套件組合奠定基礎。
匯總
如要將功能匯總到字典中,請使用相同路徑前置字元來路由這些功能:
offer: [
{
protocol: "fuchsia.logger.LogSink",
from: "#archivist",
to: "all",
as: "diagnostics/fuchsia.logger.LogSink",
},
{
protocol: "fuchsia.inspect.InspectSink",
from: "#archivist",
to: "all",
as: "diagnostics/fuchsia.inspect.InspectSink",
},
{
protocol: "fuchsia.debugdata.Publisher",
from: "#debugdata",
to: "all",
as: "diagnostics/fuchsia.debugdata.Publisher",
},
],
委派
委派作業只是將字典依原樣轉送:
offer: [
{
dictionary: "diagnostics",
from: "parent",
to: "#session",
},
],
巢狀結構
您可以將字典設為包含其他字典,方法是將巢狀字典的路徑設為巢狀字典的前置字串:
offer: [
{
dictionary: "driver-services-bundle",
from: "parent",
to: "#session-manager",
as: "session/driver-services",
},
],
擷取
只要在要擷取的字典中命名能力即可:
offer: [
{
protocol: "session-bundle/fuchsia.ui.composition.Flatland",
from: "parent",
to: "#window_manager",
as: "fuchsia.ui.composition.Flatland",
},
],
這也適用於 use:
use: [
{
protocol: "session-bundle/fuchsia.ui.composition.Flatland",
path: "/svc/fuchsia.ui.composition.Flatland",
},
],
如果字典巢狀內嵌在其他字典中,也可以進行擷取 (待辦事項:範例)
擴充功能
重新命名功能,使路徑前置字元與字典一致:
offer: [
{
protocol: "session-bundle",
from: "parent",
to: "#session-manager",
},
{
protocol: "fuchsia.ui.composition.Flatland",
as: "session-bundle/fuchsia.ui.composition.Flatland",
from: "#ui",
to: "#session-manager",
},
],
為什麼是字典,而不是目錄?
我們可以使用 fuchsia.io 目錄做為套裝組合的基本型別,而不必導入字典。從某個角度來看,這很有吸引力:目錄已經存在,並提供自己的階層式組合形式。不過,有許多反對使用目錄的論點:
- VFS 類型系統攜帶的資訊與 CF 類型系統不同;舉例來說,服務、目錄和儲存空間都會對應至 VFS 中的子目錄,即使這些是 CF 中的不同類型。
- 目錄的介面比支援能力組合所需的介面複雜許多。NODE_REFERENCE、連結、旗標、屬性、資料檔案等功能與組合用途無關。
- VFS 程式庫的大小對某些應用程式來說過大,尤其是驅動程式。因此,共用程式庫墊片 (
//sdk/lib/svc) 中的//sdk/lib/component/outgoing程式庫連結,會取代 VFS 程式庫,但會犧牲功能和透明度。 - VFS 實作並非只有一個,而是兩個獨立的 C++ 實作和一個 Rust 實作。這些實作方式有細微差異和功能落差。這不是字典的問題,因為字典的實作方式只有一種,也就是在元件執行階段。
- 如果使用目錄,要編寫程式碼產生繫結,將程式提供或使用的每項能力表示為個別語言元素,就會更加困難。
- 目錄自然不支援「彙整」或「擴充」作業。必須透過提供新目錄來模擬,其中部分節點會重新導向至舊節點,這項實作作業並不容易,而且容易出錯。
之後的作業
我們將另行提出配套設計,以解決驅動程式庫使用情境,因為單靠這項提案中的功能,無法完全解決這些問題。
這項設計可為能力轉送提供更經濟實惠的語法。能力名稱和 from 不必是個別屬性,可以合併為單一路徑,其根目錄在概念上是包含所有根目錄的字典。例如:
offer: [
{
protocol: "#ui/fuchsia.ui.composition.Flatland",
to: "#session-manager",
},
],
這個語法有個好處:自然而然地,它會概略化為允許路由整個根目錄:
offer: [
{
// Plumb all capabilities from parent to child #session-manager
dictionary: "parent",
to: "#session-manager",
},
],
此外,也值得一提的是,還有一個更通用的版本,可進一步統一語法:
route: [
{
// Path to source capability in dictionary
src: "#ui/fuchsia.ui.composition.Flatland",
// Path of target capability in dictionary
dst: "#session-manager/fuchsia.ui.composition.Flatland",
},
],
route: [
{
src: "parent",
dst: "#session-manager/parent",
},
],
既有技術和參考資料
功能套裝組合是舊概念。有許多內部前身文件提出類似的想法。