本文档介绍了组件尝试连接到其命名空间中的协议时发生的步骤。
这些步骤适用于在组件管理器下运行的以下模型:
概括来讲,以下步骤包括:
- 组件管理器会根据其清单中的
use
声明构建组件的命名空间。 - 运行后,组件会尝试在其命名空间中打开协议。
- 此
Open
请求由组件管理器接收,该管理器会执行 功能路由,以便找到提供相关功能的组件 协议 - 组件管理器绑定到提供协议的组件,并将
Open
请求连接到该组件
构建组件的命名空间
命名空间是指在组件启动时向其提供的一组目录。每个目录都与一个文件系统相关联 组件可通过该路径访问 组件。
这些目录采用通道的句柄形式,组件可以通过这些通道使用 fuchsia.io.Directory
FIDL 协议。
例如,所有组件都将收到软件包内容的句柄
它们的创建来源为 /pkg
。也就是说,组件可以查看
通过读取 /pkg/bin
的内容,可在其软件包中获得二进制文件。
组件清单中的 use
声明
确定命名空间的填充方式。使用协议功能时...
use: [
{
protocol: "fuchsia.example.Foo",
},
]
...组件管理器会在组件命名空间中添加一个
协议的父级目录。在此示例中,该协议的命名空间路径为 /svc/fuchsia.example.Foo
(默认路径分配),这意味着组件管理器将向命名空间添加 /svc
的句柄。
/svc
目录由组件管理器本身提供,而组件
经理会响应此目录的协议请求,
该组件的生命周期
命名空间中显示的内容的确切语义因功能而异 类型。例如,如果使用目录功能而不是协议 功能...
use: [
{
directory: "example-data",
rights: [ "r*" ],
path: "/example/data",
},
]
...命名空间中显示的是目录本身的句柄,而不是父目录的句柄。在此示例中,这表示
/example/data
将出现在命名空间中,而如果将该路径用于
协议功能 /example
将出现在命名空间中。
组件用于打开协议
当某个组件想要打开某个协议时,它会创建一个新的通道对,
通过 Open
请求通过其
命名空间。例如,如果组件想要打开与 /svc/fuchsia.example.Foo
的连接,则新通道对的一端将通过其命名空间中的 /svc
句柄发送。然后,组件可能会调用
通过通道使用 fuchsia.example.Foo
协议。
由于包含协议的目录 (/svc
) 由组件提供
那么,它是组件管理器,将接收新
通过组件发送的 Open
请求来发送和处理。然后,组件管理器必须确定通过此通道提供协议的组件。
Open
触发功能路由
要确定在通道上提供协议的组件,
组件管理器必须遍历组件树,遵循 offer
和
expose
声明来查找 capability 的来源。此过程称为
功能路由。
从触发功能路由的组件的父级开始,
组件管理器将检查每个组件的清单,以查找 offer
其目的地与子项匹配的声明。优惠将指定一个来源
parent
、self
或子女名称。如果此优惠是由
就会继续在树中向上移动,如果邀约来自
将从组件的某个子组件中沿着树向下走到该子节点。
当路线开始沿着树木向下走时,它会寻找expose
声明,这会指定 self
的来源或
子节点。如果功能来自子项,则组件管理器将继续运行
沿着这棵树走下去
找到源为 self
的 offer
或 expose
声明后,组件管理器便可将通道交给该组件。
如果链条在任何步骤中无效,组件管理器将记录错误并关闭从 Open
调用收到的通道。如果发生这种情况,
例如:
- 组件
C
提供了parent
中的 capability,但其父级R
未向C
提供该 capability。 - 组件
C
通过其子级D
提供了一项功能,但子级D
提供了一项功能 不向C
公开该功能。
例如,请考虑以下组件树及其清单
(为简洁起见,省略了 program
块和运行程序设置):
C
/ \
B D
/
A
A.cml:
{
// ...
capabilities: [
{
protocol: "fuchsia.example.Foo",
},
],
expose: [
{
protocol: "fuchsia.example.Foo",
from: "self",
},
],
}
B.cml:
{
// ...
expose: [
{
protocol: "fuchsia.example.Foo",
from: "#A",
},
],
children: [
{
name: "A",
url: "fuchsia-pkg://fuchsia.com/a#meta/a.cm",
},
]
}
C.cml:
{
// ...
offer: [
{
protocol: "fuchsia.example.Foo",
from: "#B",
to: [ "#D" ],
},
]
children: [
{
name: "B",
url: "fuchsia-pkg://fuchsia.com/b#meta/b.cm",
},
{
name: "D",
url: "fuchsia-pkg://fuchsia.com/d#meta/d.cm",
},
]
}
D.cml:
{
// ...
use: [
{
protocol: "fuchsia.example.Foo",
},
],
}
当 D
对其命名空间中的 /svc/fuchsia.example.Foo
调用 Open
时,组件
管理器将遍历树,以查找应提供此
协议。它将从 D
的父级 C
开始,然后:
- 查找
fuchsia.example.Foo
到D
的offer
声明,并确认它来自子B
。 - 在
B
中查找fuchsia.example.Foo
的expose
声明, 您会看到来自A
。 - 在
A
中查找fuchsia.example.Foo
的expose
声明,并确认它来自self
。也就是说,A
是组件 提供D
正在尝试使用的功能。
现在,已找到提供程序组件,组件管理器可以尝试
通过 Open
请求转交收到的频道。
绑定到组件并发送协议通道
找到提供程序后,客户端组件现已绑定到提供程序。这个 会导致组件当前停止运行,然后开始运行。
每个组件在启动后都会在其句柄表中收到指向出站目录的服务器句柄。
绑定一个组件后,组件管理器会将
协议通道发送到提供组件的传出目录(位于
来源路径:位于提供内容的组件的 offer
或 expose
声明中。
在上面的示例中,组件管理器将通过Open
组件 A
到 /svc/fuchsia.example.Foo
的传出目录句柄
路径,从而提供在组件 D
收到的频道句柄。
名为 Open
传递给组件管理器。
然后,组件 A
会接收此请求,并开始通过所提供的通道响应消息。
由于组件管理器会直接将协议通道的服务器端转发到提供方组件的传出目录,因此它不会参与消息代理,并且在完成 capability 路由后完全不在视野范围内。一旦与另一个组件建立连接,它们就会通过 相互之间直接进行,中间没有仲裁者
注意事项
运行时不可预测性
由于功能路由的运行时性质以及 组件提供功能,就无法知道 可以先从其命名空间中成功访问某功能,然后再尝试 即使存在针对相应功能的有效优惠/公开链,软件包 更新可能会在运行时破坏此链,并且完全有可能 声称在其清单中提供某项功能的组件无法做到这一点 。
提供的功能与 Ambient 权能
有些功能由组件框架本身提供, 直接使用(或将隐式提供给)没有 提供这些功能的父级发布商。目前,这些国家/地区包括:
/pkg
:用于创建组件的软件包的句柄。/svc/fuchsia.component.Realm
:一种协议,组件可以使用该协议来管理自己的领域。