RFC-0101:含編號控制代碼的動態元件

RFC-0101:具有編號控制代碼的動態元件
狀態已接受
區域
  • 元件架構
說明

提供將編號控點傳遞至動態元件的方法。

Gerrit 變更
作者
審查人員
提交日期 (年-月-日)2021-05-18
審查日期 (年-月-日)2021-06-02

摘要

本文建議在 fuchsia.sys2.Realm/CreateChild 中新增參數。這個參數會包含一組編號控點。建立的元件要求執行時,執行器會收到這些控制代碼。只有在具有新耐久性類型 (single-run) 的集合中執行的元件,才能使用提供的控制代碼。集合中耐久性single-run 的元件會在建立時啟動,停止時則會遭到刪除。這會將提供的控制代碼範圍限定為元件的單一執行作業。

提振精神

Starnix 是元件架構 v2 執行器,可在 Fuchsia 上執行 Linux 二進位檔。Starnix 會實作 Linux 系統介面,以便執行這些二進位檔,不需進行修改。

Starnix 提供 ffx 外掛程式,可讓使用者從主機指令列執行 Linux 元件。Starnix 想將 Linux 元件的 stdin/stdout/stderr 連接至一組三元組的通訊端控制代碼。這個元件不會透過公開 FIDL 通訊協定與系統互動。 而是透過提供的通訊端與使用者互動。

設計

背景

元件架構中有兩個生命週期轉換維度。 第一種與存在狀態有關 (CreatedDestroyed),第二種則與元件的執行作業有關 (StartedStopped)。在只有程序而沒有元件的其他作業系統中,這兩個維度是等效的:建立程序與啟動程序相同,而程序停止執行時就會遭到終止。

將引數提供給元件時,CreatedStarted 之間的區別非常重要。通常單一元件執行個體可以多次啟動及停止,因此元件管理員必須儲存引數,以便在每次元件執行時提供引數。如果引數是控制代碼,這可能會造成問題,因為並非所有控制代碼都能重複。因此,任何不可重複的控制代碼都會由元件的單一執行作業「耗用」。

通訊協定更新

如「背景」一節所述,建立元件與啟動元件不同。因此,提供給 CreateChild 的控制代碼必須是下列其中一種:

  1. 每次啟動元件時都會提供。
  2. 範圍限定為元件的單一執行作業。

由於並非所有控制代碼都能重複 (儲存在元件管理工具中,供後續執行),因此只有在每次執行時都從來源擷取新控制代碼,才能實現 (1)。請參閱路由控制代碼,瞭解為何未選擇在啟動時擷取控制代碼的方法。幸好 (2) 是這個用途的可行解決方案。

系統會建立新資料表 ChildArgs,並將其新增為 Realm/CreateChild 的參數:

protocol Realm {
  /// If args contains numbered_handles, the collection must have a durability
  /// of type `single-run`.
  CreateChild(CollectionRef collection, ChildDecl decl, ChildArgs args)
      -> () error fuchsia.component.Error;
}

resource table ChildArgs {
  /// The numbered handles for the component instance.
  ///
  /// Only PA_FD and PA_USER* handles are valid arguments, and inclusion of any other
  /// handles will result in an error.
  1: vector<fuchsia.process.HandleInfo>:N numbered_handles;
}

此外,fuchsia.component.runner.ComponentStartInfo 資料表也會更新,包含編號控點:

resource table ComponentStartInfo {
  6: vector<fuchsia.process.HandleInfo>:N numbered_handles;
}

如果執行元件不支援將編號控制代碼提供給元件,執行器會將這些控制代碼提供給元件,或關閉控制代碼並傳回錯誤。

穩定轉換升幅

透過 Realm 通訊協定建立的元件會位於集合中。 集合具有 durability 註解,可指出集合中元件的生命週期語意。

系統會新增 durabilitysingle-run,指出集合中的元件會在建立時立即啟動,並在停止時終止。ChildArgs.numbered_handles 只能與標示 single-run 的集合搭配使用。這會將 ChildArgs 中的引數範圍限定為元件的單一執行作業。

collections: [
    {
         name: "playground",
         durability: "single-run",
    }
],

實作

元件管理工具將更新為儲存 ChildArgs,直到傳遞至執行元件為止,並處理 single-run 集合語意。

回溯相容性

這項變更與執行器回溯相容:執行器不一定要使用提供的編號控制代碼。如果執行元件不支援編號控制代碼,則應關閉控制代碼。

這項變更不適用於 Realm 用戶端。

這項變更與 CML 回溯相容,唯一變更就是新增了耐久性列舉。

效能

由於控點是直接提供給元件管理員,因此預期不會對效能造成影響。

安全性考量

這項變更推出後,家長就能將任意編號的控制代碼傳遞給孩子。元件管理員和執行元件都會仲介這些控制代碼的交換作業。只有在標示為 single-run 的集合中執行的元件,才能以這種方式接收控制代碼。

測試

現有的 CreateChild 測試將擴大範圍,涵蓋新的引數。

缺點、替代方案和未知事項

缺點

與下列替代方案相比,建議解決方案有幾項缺點:

  • 這是啟動元件的新方式:元件的生命週期不會受到能力繫結影響。
  • 元件架構的靜態 CML 分析無法解讀提供的控制代碼。
  • 由於接收控制代碼的元件會在停止時遭到刪除,因此元件的永久儲存空間也會一併清除。因此,使用持續性儲存空間的元件不適合這項功能。

將路由編號控點視為功能

元件架構可以明確執行路由編號控制代碼。

達成這個目標的方式有很多種,但都具有下列「形狀」。

導入由編號控制代碼來源實作的通訊協定:

protocol HandleProvider {
    Get(string handle_name) -> (fuchsia.process.HandleInfo handle);
}

這項通訊協定隨後會轉換為能力:

capabilities: [
    {
        handle_provider: "stdin",
        path: "/svc/fuchsia.component.HandleProvider",
    },
],
expose: [
    {
        handle_provider: "stdin",
        from: "self",
    },
],

然後透過 CML 將這項功能傳送至目的地,就像其他功能一樣。

優點

  • 可演進為支援控點的更多類型資訊。
  • 讓元件架構明確看到控點的路由。

缺點

擷取控制代碼後,元件才能啟動。 即使效能影響可透過在元件管理員中快取控制代碼來攤銷,但由於控制代碼在每次執行元件時都會不同,因此動機用例不會從中受益。在建議的設計中,主機程式碼可以「發出並忘記」啟動元件的要求,並繼續執行,彷彿呼叫成功。在這個替代方案中,主機程式碼必須等待相關聯的控制代碼要求傳回。

透過靜態路由不一定能連上控制碼供應商。在動機明確的用途中,控制代碼供應器位於主機上,透過 Overnet 連線至 Fuchsia 裝置。如要解決這個問題,請「盡可能」進行路徑設定,然後讓「邊緣」元件透過臨時機制擷取控制代碼,再將控制代碼傳回元件架構。這會增加想使用這項功能的開發人員負擔。

控制代碼供應商無法區分來自特定能力路徑的控制代碼要求。具體來說,請考慮以下激勵人心的應用案例:

  1. 使用者從主機啟動兩個 Linux 元件,分別位於不同的終端機。
  2. 元件會在集合中例項化。
  3. 處理供應商收到相同控制代碼的兩項要求。

此時,控制碼供應商無法得知哪個元件與哪個控制碼要求相關聯。導入額外的用戶端識別機制即可解決這個問題,但這比建議的設計複雜許多。

這個解決方案的承諾較大,也比建議的設計複雜。不過,提議的設計不會妨礙或衝突日後明確路由傳送編號控制代碼。

StartChild

這個替代方案建議將編號控點新增為 RealmStartChild 呼叫的引數。這與建議的設計類似,但缺點是會導致元件繫結至元件提供的能力 (這會啟動元件) 與發出的 StartChild 呼叫之間發生競爭情形。具體來說,只有在 StartChild 呼叫贏得競爭時,元件才會收到編號控制代碼,因為如果元件已啟動,控制代碼的傳送方式就不明確。

入門通訊協定

傳遞編號控制代碼可透過 Starter 通訊協定完成。啟動器通訊協定可用於啟動元件,由元件管理員代表元件實作,且可像任何其他通訊協定一樣路由 (即元件可由非父項的元件啟動)。

這個通訊協定可像任何其他通訊協定一樣進行路由,因此用戶端可使用這個通訊協定,啟動元件階層中任意位置的元件。

啟動器通訊協定包含一個方法,可接受編號控點做為引數:

[Discoverable]
protocol StarterWithArgs {
    /// Start the component that is bound to this protocol.
    /// If the component is already running, the call returns an error.
    Start(StartArgs start_args) -> () error fuchsia.component.Error;
};

這項提案與 StartChild 非常相似。以這種方式公開的通訊協定較容易稽核及加入允許清單,但也會在呼叫 Start 與繫結至元件公開的任何其他能力之間,產生競爭條件。此外,由於用戶端不一定是父項,因此不一定能重新啟動元件來提供引數,這也加劇了排序問題。只要在通訊協定中新增停止方法,即可解決這個問題。

用戶端也必須在啟動通訊協定和領域通訊協定之間協調子項管理作業,而不是透過領域通訊協定專門管理子項。