基于组件的软件

组件是 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

此列表表示组件实例树,其中 bootstrapcorestartup 等组织组件构成了根目录下的子树。

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

请注意此处报告的一些详细信息:

  1. 组件实例的唯一标识符(称为别名)。
  2. 此组件加载的软件包网址。
  3. 组件的执行状态。
  4. 实例的当前作业/进程 ID。
  5. 为组件请求和公开的一组功能。

跟踪 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

这表示 cobaltfeedback 组件使用此 capability(即将其列在“Incoming Capabilities”下)。这些组件之间的共同祖先实体是 core,负责将此功能路由到必要的子级。