命名空间是 Fuchsia 中文件访问和服务发现的支柱。
定义
命名空间是向组件提供的文件、目录、套接字、服务、设备和其他命名对象的复合层次结构。
我们来详细了解一下。
对象具有名称:命名空间包含可按名称枚举和访问的对象,这与列出目录或打开文件非常相似。
复合层次结构:命名空间是通过将其他命名空间中的对象的子树组合成复合结构而组成的对象树,其中每个部分都已按惯例分配了路径前缀。
每个组件都有自己的命名空间:每个组件都会收到专门为其量身定制的命名空间。它还可以发布自己的对象,以便纳入其他命名空间。
您也可以独立于组件创建和使用命名空间,但本文档重点介绍的是典型的组件绑定用法。
命名空间实践
您可能已经花了一些时间探索 Fuchsia 命名空间;它们无处不在。如果您在命令行 Shell 提示符中输入 ls /
,则会看到一个列表,其中列出了可从 Shell 的命名空间访问的部分对象。
与其他操作系统不同,Fuchsia 没有“根文件系统”。如前所述,命名空间是按组件定义的,而不是全局定义或按进程定义的。
这会产生一些有趣的影响:
- 没有全局“根”命名空间。
- 不存在“在 chroot 环境中运行”的概念,因为每个组件实际上都有自己的私有“根”。
- 组件会收到根据其具体需求量身定制的命名空间。
- 跨命名空间边界的对象路径可能没有意义。
- 一个进程可以同时访问多个不同的命名空间。
- 用于控制对文件的访问权限的机制还可用于按组件控制对服务和其他命名对象的访问权限。
物体
命名空间中的项称为对象。它们有多种变种,包括:
- 文件:包含二进制数据的对象
- 目录:包含其他对象的对象
- 套接字:在打开时建立连接的对象,例如命名管道
- 服务:在打开时提供 FIDL 服务的对象
- 设备:用于提供对硬件资源的访问权限的对象
访问对象
如需访问命名空间中的对象,您必须已拥有另一个对象。在命名空间传输期间,组件通常会收到其命名空间范围内对象的通道句柄。
您还可以通过实现适当的 FIDL 协议,凭空创建新对象。
给定对象的通道后,您可以向其发送包含用于标识所需子对象的对象相对路径表达式的 FIDL 消息,以便为其子对象之一打开通道。这与在目录中打开文件非常类似。
请注意,您只能访问从您已有访问权限的对象可访问的对象。没有环境权限。
现在,我们将定义如何构建对象名称和路径。
对象名称
对象名称是本地唯一的标签,可用于在容器(例如目录)中定位对象。请注意,名称是容器的子对象表的属性,而不是对象本身的属性。
例如,cat
用于指定位于 Open()
请求的某个未指定收件人中的毛茸对象。
对象本质上是没有名称的,但其他人可能会用许多名称来称呼它们。
对象名称表示为二进制八字节字符串(任意字节序列),并遵循以下约束条件:
- 长度下限为 1 个字节。
- 长度上限为 255 个字节。
- 不包含 NUL(值为零的字节)。
- 不含
/
。 - 不等于
.
或..
。 - 始终使用逐字节比较(即区分大小写)。
对象名称是容器的 Open()
方法的有效实参。请参阅 FIDL 协议。
对象名称应编码为可被解读为 UTF-8 图形字符序列的人类可读内容,但命名空间本身不会强制执行此属性。
因此,客户端负责决定如何向用户呈现包含无效、无法显示或模糊字符序列的名称。
对象相对路径表达式
对象相对路径表达式是指对象名称或以 /
分隔的对象名称序列,用于指定要遍历的嵌套对象序列,以便在容器(例如目录)中找到对象。
例如,house/box/cat
用于指明位于其包含对象 box
中的毛茸对象,该包含对象位于其包含对象 house
中,而 house
位于 Open()
请求的某个未指定的接收器中。
对象相对路径表达式始终会深入到命名空间中。值得注意的是,该命名空间不直接支持从容器中向上遍历(例如通过 ..
),但客户端可以部分模拟此功能(见下文)。
相对于对象的路径表达式具有以下额外限制:
- 长度下限为 1 个字节。
- 长度上限为 4095 个字节。
- 不以
/
开头或结尾。 - 所有细分都是有效的对象名称。
- 始终使用逐字节比较(即区分大小写)。
相对于对象的路径表达式是容器的 Open()
方法的有效参数。请参阅 FIDL 协议。
客户端解读的路径表达式
客户端解释的路径表达式是对对象相对路径表达式的概括,但包含可由客户端代码模拟的可选功能,以提高与预期使用已取得 root 权限的文件类接口的程序的兼容性。
从技术层面来说,这些功能超出了 Fuchsia 命名空间协议本身的范围,但它们经常被使用,因此我们在此处对其进行了介绍。
- 客户端可以指定其某个命名空间用作其“根”。
此命名空间表示为
/
。 - 客户端可以通过在前面附加单个
/
来构建相对于其指定根命名空间的路径。 - 客户端可以通过一种称为客户端“规范化”的过程,将路段折叠在一起(假设容器的路径已知),从而使用
..
路段构建从容器向上遍历的路径。 - 这些功能可以组合使用。
例如,/places/house/box/../sofa/cat
用于指定位于某个客户端指定的“根”容器内的 places/house/sofa/cat
处的毛绒对象。
包含这些可选功能的客户端解读的路径表达式不是容器的 Open()
方法的有效实参;客户端必须先对这些表达式进行转换,然后才能与命名空间进行通信。请参阅 FIDL 协议。
例如,fdio
会在文件操作 API(例如 open()
、stat()
、unlink()
等)中实现对 ..
路径的客户端解析。
命名空间转移
组件启动(例如,其进程启动)时,它会收到一个表,该表会将一个或多个命名空间路径前缀映射到对象句柄。
表格中的路径前缀会按照惯例编码其关联对象的预期重要性。例如,pkg
前缀应与目录对象相关联,该目录对象包含组件自己的二进制文件和资源。
下一部分将对此进行详细介绍。
命名空间惯例
本部分介绍了在 Fuchsia 上运行的典型组件的命名空间的传统布局。
组件的命名空间的确切内容和组织方式因组件的角色、类型、身份、范围、与其他组件的关系和权限而异。如需了解如何使用命名空间为组件创建沙盒,请参阅沙盒。
如需详细了解组件预计会收到的命名空间,请参阅与您要实现的组件类型相关的文档。
典型对象
组件命名空间可能包含一些典型对象:
- 组件软件包中的只读可执行文件和资源。
- 私有本地永久性存储空间。
- 私密临时存储空间。
- 系统、组件框架或启动组件的客户端向组件提供的服务。
- 设备节点(适用于驱动程序和特权组件)。
- 配置信息。
典型目录结构
pkg/
:当前程序软件包的内容(签名软件包时的内容)bin/
:软件包中的可执行二进制文件lib/
:软件包中的共享库data/
:软件包中的数据(例如资源)
data/
:本地永久性存储(读写,对组件私有)tmp/
:临时存储空间(读写,对组件私有)svc/
:向组件提供的协议和服务fuchsia.process.Launcher
:启动进程fuchsia.logger.Log
:日志消息vendor.topic.Interface
:由供应商定义的服务
dev/
:设备树(根据需要向特权组件显示的相关部分)class/
...
config/
:组件的配置数据build-info/
:build 信息文件的规范路径。ssl/
:SSL 证书的规范路径tzdata/
:时区数据文件。第一个子路径应为数据格式名称,例如tzif2/...
或icudata/...
。
命名空间参与者
以下是与 Fuchsia 命名空间协议交互和支持该协议的一些抽象的更多信息。
文件系统
文件系统可在命名空间中提供文件。
文件系统只是一个组件,用于从其他人的命名空间发布类似文件的对象。
协议
协议句柄是指由 FIDL 协议实现支持的通道对象,可使用命名空间进行发现。协议名称对应于命名空间的 /svc
分支中的路径,组件可以通过该路径访问实现。
例如,默认的 Fuchsia 日志记录协议为 fuchsia.logger.Log
,其在命名空间中的路径为 /svc/fuchsia.logger.Log
。
如需详细了解协议和组件,请参阅协议功能。
服务
服务 通过命名空间访问。
服务句柄是包含相关 FIDL 协议的目录。您可以使用该命名空间发现这些协议的实现。服务名称对应于命名空间的 /svc
分支中的路径,组件可以通过该路径访问实现。
如需详细了解服务和组件,请参阅服务功能。
组件
组件会使用和扩展命名空间。
组件是指在某个拓扑中实例化并分配了命名空间的可执行程序对象。
组件可通过以下两种方式参与 Fuchsia 命名空间:
- 它可以使用提供给它的命名空间中的对象,例如传入协议和服务或它自己的软件包内容。
- 它可以通过其传出目录以命名空间的形式将对象发布到其他组件。