RFC-0041:支援統合服務和裝置 | |
---|---|
狀態 | 已接受 |
領域 |
|
說明 | 說明服務的概念:一組通訊協定,其中可能有一或多個集合。 |
作者 | |
提交日期 (年-月-日) | 2019-04-08 |
審查日期 (年-月-日) | 2019-04-23 |
摘要
說明服務的概念 - 一組通訊協定,其中 也有可能是集合的一或多個實例。
提振精神
目前在元件架構中,服務已定義為
而且該通訊協定的例項只會存在於該通訊協定的一個執行個體
/svc
下程序的命名空間。
否則我們無法說明更複雜的關係:
- 根據
分別在兩個不同版本
通訊協定,例如
FontProvider
和FontProviderV2
- 為了根據
存取層級 — 例如:一般存取權與管理員
例如
Directory
和DirectoryAdmin
,其中後者提供了 特殊權限存取 - 服務由許多不同的通訊協定所組成
不同的消費者 — 例如使用
Power
管理電源管理,以及Ethernet
適用於網路堆疊 - 含有多個執行個體的服務,例如多音訊
提供「
AudioRenderer
」的裝置,或多部印表機暴露在Printer
的風險中
只要提供這樣的彈性,服務就能更加清楚明確。
或是使用 service
中樞。
透過這樣的靈活性,我們就能將裝置定義為服務。
具體來說,我們打算改善 /svc/
$Protocol
這意味著「每個程序命名空間只有一個通訊協定」改為:
/svc/$Service/$Instance/$Member
而會產生兩個額外的指示:服務 (例如 印表機、乙太網路) 和執行個體 (例如預設、deskjet_by_desk e80::d189:3247:5fb6:5808). 通訊協定的路徑由以下部分組成:
$Service
:服務的完整類型, 在 FIDL 中宣告$Instance
:服務的執行個體名稱,其中 「預設」,以指出偏好 (或只) 使用 可用的執行個體$Member
— 與 FIDL 中宣告的服務成員名稱,其中 該成員宣告的類型,表示預期的通訊協定
設計
服務型態
首先,我們來思考我們希望支援的各種服務:
單一且不重複的通訊協定:ONE 執行個體、ONE 通訊協定:
/svc/fuchsia.Scheduler/default/profile_provider
由多個通訊協定組成:ONE 個執行個體、MANY 個通訊協定:
/svc/fuchsia.Time/default/network .../rough
服務的多個執行個體,設有單一通訊協定:MANY 個執行個體,ONE 通訊協定:
/svc/fuchsia.hardware.Block/0/device .../1/device
多個含有不同通訊協定組合的執行個體:MANY 個執行個體,MANY 個通訊協定:
/svc/fuchsia.Wlan/ff:ee:dd:cc:bb:aa/device .../power .../00:11:22:33:44:55/access_point .../power
語言
向 FIDL 介紹服務的概念並支援各種 我們將對 FIDL 語言進行下列變更:
- 新增
service
關鍵字。 - 移除
Discoverable
屬性。
service
關鍵字會允許我們撰寫服務宣告,
我們可以用來將一組通訊協定定義為服務成員。
舉例來說,我們可以宣告不同的服務型態,如下所示:
單一且不重複的通訊協定:ONE 執行個體、ONE 通訊協定:
service Scheduler { fuchsia.scheduler.ProfileProvider profile_provider; };
由多個通訊協定組成:ONE 個執行個體、MANY 個通訊協定:
service Time { fuchsia.time.Provider network; fuchsia.time.Provider rough; };
服務的多個執行個體,設有單一通訊協定:MANY 個執行個體,ONE 通訊協定:
service Block { fuchsia.hardware.block.Device device; };
多個含有不同通訊協定組合的執行個體:MANY 個執行個體、MANY 個通訊協定
service Wlan { fuchsia.hardware.ethernet.Device device; fuchsia.wlan.AccessPoint access_point; fuchsia.hardware.Power power; };
服務宣告可能含有多位成員 但每個成員宣告必須使用不同的 ID。 請參閱「包含多個通訊協定的組合」。
當服務的執行個體包含一組不同的通訊協定時 服務宣告會宣告所有可能的 所有執行個體中可能存在的通訊協定。 請參閱「通訊協定組合不同的多個執行個體」一節。
服務宣告不會提及特定執行個體的名稱 服務或元件的 URI 到目前為止根據元件資訊清單列出元件架構 宣告,並在執行階段使用其 API。
語言繫結
系統將修改語言繫結,以便更連線至服務 非常便利 具體來說,這些技術將更加註重服務,例如:
連接至「預設」設有單一通訊協定的服務執行個體:ONE 執行個體、ONE 通訊協定:
- C++:
Scheduler scheduler = Scheduler::Open(); ProfileProviderPtr profile_provider; scheduler.profile_provider().Connect(profile_provider.NewRequest());
- Rust:
let scheduler = open_service::<Scheduler>(); let profile_provider: ProfileProviderProxy = scheduler.profile_provider();
連接至「預設」具有多個通訊協定的服務執行個體:ONE instance, MANY 個通訊協定:
- C++:
Time time = Time::Open(); ProviderPtr network; time.network().Connect(&network); ProviderPtr rough; time.rough().Connect(&rough);
- Rust:
let time = open_service::<Time>(); let network = time.network(); let rough = time.rough();
透過單一通訊協定 (MANY 個執行個體、ONE 通訊協定,連至多個執行個體:
- C++:
Block block_0 = Block::OpenInstance("0"); DevicePtr device_0; block_0.device().Connect(&device_0); Block block_1 = Block::OpenInstance("1"); DevicePtr device_1; block_1.device().Connect(&device_1);
- Rust:
let block_0 = open_service_instance::<Block>("0"); let device_0 = block_0.device(); let block_1 = open_service_instance::<Block>("1"); let device_1 = block_1.device();
連線至服務的多個執行個體,其中的多個通訊協定為 MANY 個執行個體,MANY 個通訊協定:
- C++:
Wlan wlan_a = Wlan::OpenInstance("ff:ee:dd:cc:bb:aa"); DevicePtr device; wlan_a.device().Connect(&device); Power power_a; wlan_a.power().Connect(&power_a); Wlan wlan_b = Wlan::OpenInstance("00:11:22:33:44:55"); AccessPoint access_point; wlan_b.access_point().Connect(&access_point); Power power_b; wlan_b.power().Connect(&power_b);
- Rust:
let wlan_a = open_service_instance::<Wlan>("ff:ee:dd:cc:bb:aa"); let device = wlan_a.device(); let power_a = wlan_a.power(); let wlan_b = open_service_instance::<Wlan>("00:11:22:33:44:55"); let access_point = wlan_b.access_point(); let power_b = wlan_b.power();
以下說明建議的函式簽章。
請注意,Open()
和 OpenInstance()
方法也接受
選用參數,可指定命名空間。
根據預設,系統會使用程序的全域命名空間 (可以擷取
使用 fdio_ns_get_installed)。
// Generated code.
namespace my_library {
class MyService final {
public:
// Opens the "default" instance of the service.
//
// |ns| the namespace within which to open the service or nullptr to use
// the process's "global" namespace as defined by |fdio_ns_get_installed()|.
static MyService Open(fdio_ns_t* ns = nullptr) {
return OpenInstance(fidl::kDefaultInstanceName, ns);
}
// Opens the specified instance of the service.
//
// |name| the name of the instance, must not be nullptr
// |ns| the namespace within which to open the service or nullptr to use
// the process's "global" namespace as defined by |fdio_ns_get_installed()|.
static MyService OpenInstance(const std::string& instance_name,
fdio_ns_t* ns = nullptr);
// Opens the instance of the service located within the specified directory.
static MyService OpenAt(zxio_t* directory);
static MyService OpenAt(fuchsia::io::DirectoryPtr directory);
// Opens a directory of available service instances.
//
// |ns| the namespace within which to open the service or nullptr to use
// the process's "global" namespace as defined by |fdio_ns_get_installed()|.
static fidl::ServiceDirectory<MyService> OpenDirectory(fdio_ns_t* ns = nullptr) {
return fidl::ServiceDirectory<MyService>::Open(ns);
}
// Gets a connector for service member "foo".
fidl::ServiceConnector<MyService, MyProtocol> foo() const;
// Gets a connector for service member "bar".
fidl::ServiceConnector<MyService, MyProtocol> bar() const;
/* more stuff like constructors, destructors, etc... */
}
而繫結程式碼則是:
/// FIDL bindings code.
namespace fidl {
constexpr char[] kDefaultInstanceName = "default";
// Connects to a particular protocol offered by a service.
template <typename Service, typename Protocol>
class ServiceConnector final {
public:
zx_status_t Connect(InterfaceRequest<Protocol> request);
};
// A directory of available service instances.
template <typename Service>
class ServiceDirectory final {
public:
// Opens a directory of available service instances.
//
// |ns| the namespace within which to open the service or nullptr to use
// the process's "global" namespace as defined by |fdio_ns_get_installed()|.
static ServiceDirectory Open(fdio_ns_t* ns = nullptr);
// Gets the underlying directory.
zxio_t* directory() const;
// Gets a list of all available instances of the service.
std::vector<std::string> ListInstances();
// Opens an instance of the service.
Service OpenInstance(const std::string& name);
// Begins watching for services to be added or removed.
//
// Invokes the provided |callback| to report all currently available services
// then reports incremental changes. The callback must outlive the returned
// |Watcher| object.
//
// The watch ends when the returned |Watcher| object is destroyed.
[[nodiscard]] Watcher Watch(WatchCallback* callback,
async_dispatcher_t* dispatcher = nullptr);
// Keeps watch.
//
// This object has RAII semantics. The watch ends once the watcher has
// been destroyed.
class Watcher final {
public:
// Ends the watch.
~Watcher();
};
// Callback invoked when service instances are added or removed.
class WatchCallback {
public:
virtual void OnInstanceAdded(std::string name) = 0;
virtual void OnInstanceRemoved(std::string name) = 0;
virtual void OnError(zx_status_t error) = 0;
};
}
語言繫結除了可以進一步延伸, 反覆探索服務的執行個體,看看 執行個體才會變為可用
服務演進
為了改進服務,我們可以逐步在服務中加入新的通訊協定。 為了維持原始碼相容性,現有通訊協定不應 否則原始碼相容性可能會因為使用者 取決於語言繫結從服務產生的程式碼。
由於服務中的所有通訊協定都是選擇性的,因此可以 。 最終的發展 AI 可簡化我們在 AI 技術發展 服務:
- 您隨時可以將通訊協定成員新增至服務
- 為避免來源相容性,請避免移除通訊協定成員
- 將通訊協定成員重新命名時,需要新增通訊協定成員,且 退出現有的通訊協定成員
為了改進服務本身,我們訂有類似的限制。 服務不保證一定會存在於元件的命名空間中,且 服務可以顯示在命名空間中的多個不同位置 因此:
- 你隨時可以新增服務
- 為確保來源相容性,請避免移除服務
- 如要將服務重新命名,包括複製服務,以及使用新的 名稱,同時保留服務的原始副本 (用於原始碼) 相容性)
可能的擴充功能
預期 service
個例項最終會成為「第一類別」且
就像 protocol P
帳號一樣可加入訊息
以 P
或 request<P>
的形式傳遞。
這可能會以 service_instance<S>
之類的形式,
service S
。
我們會盡力擴充這個擴充功能,但不會加以運作
。
我們讓這個大門保持開啟 (並計劃) 拓展會員類型
而非僅允許通訊協定。
舉例來說,我們可能希望讓服務公開 VMO (handle<vmo>
):
service DesignedService {
...
handle<vmo>:readonly logo; // gif87a
};
執行策略
本提案應分階段實施,以免違反現有提案 再也不是件繁重乏味的工作
第 1 階段
- 修改 component_manager,讓元件 v2 支援新的 服務目錄結構定義
- 修改 appmgr 和 sysmgr,讓元件 v1 支援新的 服務目錄結構定義
第 2 階段
- 新增服務宣告支援。
- 修改語言繫結來產生服務。
第 3 階段
- 針對具有
Discoverable
屬性的所有通訊協定,建立 適當的服務宣告 >注意:在這個階段,我們會確認沒有名稱 >服務的新目錄結構定義可能發生衝突。 - 遷移所有原始碼以使用服務。
第 4 階段
- 請從 FIDL 檔案中移除所有
Discoverable
屬性。 - 從 FIDL 和語言繫結移除對
Discoverable
的支援。 - 移除 component_manager 對舊目錄結構定義的支援。 appmgr 和 sysmgr。
說明文件和範例
因此我們必須展開 FIDL 教學課程,解釋服務的使用狀況 宣告內容,以及它們與通訊協定的互動方式。 接著,我們會說明服務的不同結構:單例模式和 以及語言繫結的使用方式。
詞彙解釋
通訊協定宣告說明系統可能傳送或 以及各自的二進位表示法
服務宣告說明瞭以單元形式提供的能力 由服務供應商提供 包含服務名稱,以及零或多個已命名的成員通訊協定 以及用戶端與能力互動的方式
同一個通訊協定可能會以服務成員的形式出現多次 ,其中成員姓名表示預定的解讀 通訊協定的依附元件清單
service Foo {
fuchsia.io.File logs;
fuchsia.io.File journal;
};
元件宣告是指一個可執行軟體的單位 包括元件二進位檔的位置 打算使用、公開或優惠到 其他元件
這項資訊通常會編碼為元件資訊清單檔案 套件中:
// frobinator.cml
{
"uses": [{ "service": "fuchsia.log.LogSink" }],
"exposes": [{ "service": "fuchsia.frobinator.Frobber" }],
"offers": [{
"service": "fuchsia.log.LogSink",
"from": "realm",
"to": [ "#child" ]
}],
"program": { "binary": ... }
"children": { "child": ... }
}
服務執行個體是符合指定服務的能力 宣告內容 在 Fuchsia 中,會以目錄的形式表示。 其他系統可能會使用不同的服務探索機制。
元件執行個體是元件的特定執行個體 或自有隱私沙箱 在執行階段,它會使用其他元件提供的服務執行個體 會在新的命名空間中開啟目錄。 相反地,此方法也會向其他元件公開自己的服務執行個體 ,使其在傳出目錄中存取這些檔案。 元件管理員會做為服務探索的代理程式。
- 元件執行個體通常 (但不一定) 對元件執行個體 。
- 元件執行器通常可以在 與「自己的」傳入命名空間都是相同的程序。
慣用服務的使用方式
回溯相容性
此提案將會淘汰,最終移除Discoverable
屬性。
電匯格式則維持不變。
如要導入新的資料類型或語言功能,請考量以下幾點: 您預期使用者會在沒有定義的 FIDL 定義的情況下進行 FIDL 定義 藉此破壞產生的程式碼 如果您的功能會影響任何新的來源相容性 產生語言繫結的限制,請在這裡列出。
成效
這應該不會影響連接 服務的預設執行個體,或稱為 Pori 的執行個體。
連線至無法辨識的執行個體 ID 的其他執行個體 aprii,使用者必須列出服務的目錄並定位 才能連線
這對建構和二進位檔大小的影響微乎其微 定義必須由後端產生特定語言繫結的定義。
安全性
本提案將協助我們強制執行更精細的存取權控管機制 因為我們可以將服務分割成分別具有不同存取能力的通訊協定 權利。
此提案不會對安全性產生其他影響。
測試
編譯器中的單元測試,並變更為相容性測試套件 檢查服務包含的通訊協定是否可連線。
缺點、替代方案和未知
我們探討了以下問題:
- 服務宣告為何會出現在 FIDL 中?
- 通訊協定、服務和 元件?
- 提議的平面拓撲是否有足夠的服務執行個體 您的表現呢?
- 我們應如何持續拓展服務?
- 如果元件執行個體想公開多個 單一基礎邏輯資源,該以什麼方式表示?
問題 1:服務宣告為何會出現在 FIDL 中?
回應
- 我們使用 FIDL 描述 Fuchsia 的系統 API,包括通訊協定 與元件交換的環境
- 視情況而定,許多方式可能會運用相同的通訊協定。 代表這些通訊協定的各種用途,使其成為服務 可讓開發人員輕鬆存取適合 情境。
- FIDL 已提供可立即擴充的語言繫結 讓開發人員以一致且方便的方式 免費 Google Cloud 服務
討論
- [ianloic] 那麼有關元件資訊清單呢?為何不將 FIDL 用於 我也跟你說明一下嗎?
- [jeffbrown] 元件資訊清單說明超出處理序間通訊 (IPC) 以外的概念 個疑慮
- [abdulla] 說明元件資訊清單中的服務會帶來 重複的服務說明
- 我們可以透過資訊清單產生元件的骨架嗎?
- [drees] 在 FIDL 中加入服務宣告,僅是 這點是否合理?
- [jeffbrown] 我們希望服務宣告屬於元件外部 因為需要在不同元件之間共用 服務交換協議
- [ianloic] 超網服務宣告可能相近
- [pascallouis] 根據我們所知需求, 您的兒時朋友不久後就會遭到解僱 並要求您暫時保密我們之後有需要再視情況調整。
- [pascallouis] FIDL 是 Fuchsia 最先驅,因此引人入勝 功能提供的功能,根據我們 但也可以視其他情境將長期趨勢
- [dustingreen] 如果是獨立的檔案呢?
- [pascallouis] 那些檔案很小而孤獨, 對於靜態型別檢查我們是否保留在 FIDL 中,則遷移後似乎是低風險 可以視需要
問題 2:通訊協定、服務和元件有何不同?
回應
- 通訊協定宣告說明一組可能 及其二進位表示法
- 能力宣告是指以
每個服務供應商
包含服務名稱,以及零或多個已命名的成員通訊協定
用戶端儲存空間和用戶端能力
- 同一個通訊協定可能會重複出現在
服務聲明;成員名稱會指出
通訊協定的解釋
- 例如:
service Foo { fuchsia.io.File logs; fuchsia.io.File journal; };
- 例如:
- 同一個通訊協定可能會重複出現在
服務聲明;成員名稱會指出
通訊協定的解釋
元件宣告是指一個可執行軟體的單位 包括元件二進位檔的位置 打算使用、公開或優惠到 其他元件
這項資訊通常會編碼為元件資訊清單 檔案。 範例:
// frobinator.cml { "uses": [{ "service": "fuchsia.log.LogSink" }], "exposes": [{ "service": "fuchsia.frobinator.Frobber" }], "offers": [{ "service": "fuchsia.log.LogSink", "from": "realm", "to": [ "#child" ]}], "program": { "binary": ... } "children": { "child": ... } }
服務執行個體是符合指定物件的能力 服務宣告 在 Fuchsia 中,會以目錄的形式表示。 其他系統可能會使用不同的服務探索機制。
元件執行個體是元件中特定元件的執行個體 和各自的私密沙箱 執行階段會使用其他元件提供的服務執行個體 開啟其收到的命名空間中的目錄。 相反地,它也會向其他元件公開自己的服務執行個體 存取這類目錄: 元件管理員會做為服務探索的代理程式。
- 元件執行個體通常 (但不一定) 對元件執行個體 。
- 元件執行器通常可以執行多個元件執行個體 具備「專屬」連入命名空間相同的程序,
討論
- [韓文] 選擇通訊協定組合時,我們該提供什麼指引 還是服務宣告?
- [abdulla] 通訊協定組成表示通訊協定本身 代表了一組功能最強的功能 (可能無關) 分別提供
- [pascallouis] 用單一管道編寫多工通訊協定 訊息排序和服務個別通訊協定的影響 擁有不同頻道
- [jeffbrown] 可在各種不相關、組合的情況下委派代表 無法使用這項功能,服務允許「探索」執行時 例如:列出哪些通訊協定
問題 3:我們提出的一般拓撲是否足夠表達服務執行個體?
回應
- 平面拓撲很容易使用,因為不需要以遞迴方式進行 找出所有執行個體 這會影響使用便利性和效能。
- 平地拓撲可做為階層式拓撲的表達
表示相關資訊已編碼入執行個體名稱中,例如
/svc/fuchsia.Ethernet/
rack.5,port.9/packet_receiver
。 - 您可以透過 Open()、 Open(命名空間) 和 OpenAt(directory)。 換句話說,並非所有服務都須來自 `/svc"的 程序的全域命名空間 這麼做就能建立任意服務拓撲 無從得知
問題 4:我們應如何持續擴展服務?
回應
- 我們可以在現有的服務宣告中新增成員。 新增成員不會破壞來源或二進位檔相容性 因為每個成員都是選擇性的 (嘗試連線至 「通訊協定」為可失敗的作業)。
- 我們可以將現有成員從服務聲明中移除。 移除 (或重新命名) 現有成員可能會導致來源和二進位檔中斷 相容性,並可能需要製定嚴謹的遷移計畫以減少不良影響 這種做法
- 服務的說明文件應清楚說明服務方式 設計或實作服務,尤其是在這類情況下 不會顯而易見,例如說明服務有哪些功能 且日後將不再提供。
預期的版本管理模式:將新成員新增至服務,做為 持續改良通訊協定。 通訊協定列舉 (列出目錄) 可讓用戶端找出 支援的文件類型 範例:
版本 1...
service Fonts { FontProvider provider; }; protocol FontProvider { GimmeDaFont(string font_name) -> (fuchsia.mem.Buffer ttf); };
在第 2 版中,漸進式更新...
service Fonts { FontProvider provider; FontProvider2 provider2; }; protocol FontProvider2 { compose FontProvider; GetDefaultFontByFamily(string family) -> (string family); };
在第 3 版中,全新改版...
service Fonts { [Deprecated] FontProvider provider; [Deprecated] FontProvider provider2; TypefaceChooser typeface_chooser; } protocol TypefaceChooser { GetTypeface(TypefaceCriteria criteria); }; table TypefaceCriteria { 1: Family family; 2: Style style; 3: int weight; };
問題 5:如果元件執行個體想要公開與單一基礎邏輯資源相關的多項服務,該以何種形式呈現?
回應
元件可定義多個服務 元件資訊清單。 範例:
// frobinator.cml { ... "exposes": [ { "service": "fuchsia.frobinator.Fooer" }, { "service": "fuchsia.frobinator.Barer" }, ], ... }
接著,元件還會實作這些服務 但這些服務的使用者不需要留意 資訊。