组件是 Fuchsia 中运行的软件的基本构建块。每个组件都是一个可组合、沙盒化模块,可通过 capability 与其他组件交互。这有助于提高系统安全性,并在各个组件之间创建清晰的接口,使它们更易于更新或替换。
在 Fuchsia 中,几乎所有内容都是组件。通过之前对 Zircon 的讨论,我们特意将内核的表面区域设计得很小,而大多数核心服务都是在用户空间中实现的。这意味着,在 Fuchsia 上运行的大多数软件都是使用组件框架实现的,包括:
- 面向用户的应用
- 设备驱动程序
- 文件系统
- 媒体编解码器
- 网络堆栈
在内核之外,只有少数底层异常不使用组件框架,例如引导加载程序和 userboot
进程。
组件管理器
组件框架的核心是组件管理器。它负责协调所有组件实例的执行,为它们提供其功能,以及中介组件之间的连接。
组件可以显式启动(例如通过网址),也可以通过请求特定 capability 来隐式启动。组件管理器会执行必要的解析,以确定是启动新组件还是将请求路由到现有实例。为了实现此路由,每个组件都必须声明它向系统提供的所有 capability 以及它使用的所有 capability。
组件管理器会解析每个组件的声明,以确定如何运行组件并提供必要的功能。组件通常通过组件软件包中的组件清单文件向系统声明。
以下是一个组件清单的简单示例,其中描述了一个 ELF 可执行文件以及一些额外的命令参数:
program: {
runner: "elf",
binary: "bin/hello",
args: [ "Hello", "World!" ],
},
请注意,运行时声明会告知组件管理器此组件需要 ELF 运行程序。这是 capability 的示例!
组件功能
组件通过功能获得访问更大系统的各个部分的权限。每个组件都可以声明它们向系统提供的新功能,以及其他组件(或框架)提供的它们正常运行所需的功能。
如您所见,runner
是一个用于声明组件使用的运行时的功能示例。常见 capability 类型的其他示例包括用于访问文件系统资源的 directory
,以及用于与其他组件通信的 protocol
。
开发者使用组件清单声明组件所需的 capability 类型。以下示例展示了一个请求两项功能的组件清单:对 example-data
目录和 fuchsia.example.Foo
FIDL 协议描述的服务的读取访问权限。
use: [
{
directory: "example-data",
rights: [ "r*" ],
path: "/example/data",
},
{
protocol: "fuchsia.example.Foo",
},
]
组件管理器使用 capability 声明为每个组件的命名空间填充必要的目录句柄。对于此示例,组件将在其命名空间中收到 /example/data
和 /svc/fuchsia.example.Foo
。
组件组织
系统中的所有组件都会组合到一个根组件实例树中。这种树结构决定了组件行为的几个重要方面。
树中的父组件负责创建其他组件的实例作为其子组件,并为这些组件提供必要的功能。同时,子组件可以将功能公开回给父级。您可以通过以下两种方式之一创建子组件:
- 静态:父级在其自己的组件声明中声明子级的存在。
- 动态:父级使用
fuchsia.component.Realm
协议在运行时将子级添加到组件集合。
任何父级组件及其所有子级都会在树中构成一个组,称为“领域”。借助 Realm,父级可以控制哪些功能会流入和流出其组件子树,从而创建功能边界。组件使用 expose
关键字决定是否将功能导出到其领域之外:
expose: [
{
protocol: "fuchsia.example.Foo",
from: "self",
},
],
将 capability 公开给 realm 后,父级可以与同一 realm 中的其他组件共享该 capability。这通过使用 offer
关键字来完成:
offer: [
{
protocol: "fuchsia.example.Foo",
from: "self",
},
],
这称为 capability routing。组件管理器只能解析在同一领域中公开和提供的功能。
练习:组件
在本练习中,您将探索组件实例树,并详细了解使用一些核心系统组件实现功能路由的运作方式。
启动模拟器
如果您还没有运行的实例,请启动具有网络支持的 FEMU:
ffx emu start workstation_eng.x64 --headless
探索系统组件
打开另一个终端窗口,并使用 component list
命令转储系统的组件树:
ffx component list
您应该会看到类似于以下(截断)列表的输出:
/
/bootstrap
/bootstrap/archivist
/bootstrap/base_resolver
/bootstrap/console
/bootstrap/console-launcher
/bootstrap/decompressor
/bootstrap/device_name_provider
/bootstrap/driver_manager
/bootstrap/fshost
/bootstrap/kernel_debug_broker
/bootstrap/miscsvc
/bootstrap/netsvc
/bootstrap/power_manager
/bootstrap/ptysvc
/bootstrap/pwrbtn-monitor
/bootstrap/shutdown_shim
/bootstrap/sysinfo
/bootstrap/virtual_console
/core
/core/activity
...
/core/detect
/core/font_provider
/core/log-stats
/core/remote-control
/core/sampler
/core/system-update-committer
/core/temperature-logger
/core/test_manager
/core/full-resolver
/startup
此列表表示组件实例树,其中 bootstrap
、core
和 startup
等组织组件构成了根目录下的子树。
component show
命令会提供有关每个组成部分的更多详细信息。
使用以下命令查看 http-client
(提供 HTTP 请求服务的组件)的详细信息:
ffx component show http-client.cm
该命令会输出以下报告:
Moniker: /core/network/http-client
URL: #meta/http-client.cm
Type: CML static component
Component State: Resolved
Incoming Capabilities: config
fuchsia.logger.LogSink
fuchsia.net.name.Lookup
fuchsia.posix.socket.Provider
pkg
Exposed Capabilities: fuchsia.net.http.Loader
Merkle root: d9e73f5b061f2f227e596e2e0079ff3a095fc69e192cf85e0d7621826c76356c
Execution State: Running
Start reason: '/core/feedback' requested capability 'fuchsia.net.http.Loader'
Running since: ...
Job ID: 41268
Process ID: 41311
Outgoing Capabilities: fuchsia.net.http.Loader
请注意此处报告的一些详细信息:
- 组件实例的唯一标识符(称为别名)。
- 此组件加载的软件包网址。
- 组件的执行状态。
- 实例的当前作业/进程 ID。
- 为组件请求和公开的一组功能。
跟踪 capability 路线
在上面的输出中,列出了三个 capability 组:
- 传入的功能:组件使用
use
声明的功能。这些信息会通过组件的命名空间提供给组件。 - 传出功能:组件已发布到其传出目录中的功能。
- 公开的功能:组件使用
expose
声明的功能。这些是组件的公开服务。
http-client
向其父 realm 公开的功能之一是 fuchsia.net.http.Loader。这样,其他组件就可以发出 HTTP 请求了。
使用 component capability
命令确定有多少个组件与此 capability 进行交互:
ffx component capability fuchsia.net.http.Loader
该命令会列出所有匹配的组件:
Exposed:
/core/network/http-client
/core/network
Used:
/core/cobalt
/core/feedback
/core
这表示 cobalt
和 feedback
组件使用此 capability(即将其列在“Incoming Capabilities”下)。这些组件之间的共同祖先实体是 core
,负责将此功能路由到必要的子级。