FIDL设计原则

本页总结了一些FIDL随着时间推移采用的核心设计原则。

最终用户优先

FIDL是意在遵从于以下用户优先次序

  1. 用户(使用Fuchsia产品)

  2. 终端开发者(使用FIDL绑定)

  3. Fuchsia贡献者(使用FIDL绑定)

  4. API设计者(创作FIDL库)

  5. [Fuchsia FIDL组]成员

该列表根据[API协会章程]改编

优先ABI,其次API

来自:RFC-0050: Syntax revamp:

FIDL主要优先关注应用二进制接口(ABI),其次关注的是应用程序接口(API)

二进制线型格式优先

来自:RFC-0050: Syntax revamp:

尽管有多种格式可以代表FIDL消息,我们选择FIDL Wire Format(或者”FIDL二进制线型格式”)作为推荐使用格式并优先满足…在进行语法选择时,选择转换到二进制ABI的格式。

更少特性

来自:RFC-0050: Syntax revamp:

我们力求采用最少的特性和规则,并计划以合并特性方式实现用例。在实际使用中考虑新特性时,我们应当首先试图调整或归纳其他已有的特性,而不是引入新特性。

你仅需为所得付出代价{#you-only-pay}

来自:RFC-0027: You only pay for what you use:

当增加功能到FIDL中,我们应当评估增加该功能是否会给使用此FIDL但不使用此新功能的人带来开销。那么,我们就应该设定一个很高的标准来接受该功能,使不使用这些功能的人不受到影响。

例如, RFC-0047: Tables 遵循该原则追加列表到语言中,而不是替换结构。

列表必然比结构体复杂,因此处理它们会变慢,序列化它们会占用更多空间。正因如此,我们更倾向保持结构不变,并引入一些新的东西。

相比之下,RFC-0061: Extensible unions 经过仔细的权衡分析,则采用不同策略,使用扩展联合体来取代静态联合体。在大多数情况下,对比列表而言。扩展联合体的额外开销是微不足道的或者不存在的。

解决实际问题

我们设计FIDL就是为了解决实际问题和真实需求,而不是想象的问题。我们需避免“工具规律”(solution in search of a problem)。

例如,FIDL最初不支持空结构体是因为在C/C++中不能清楚体现。在RFC-0056: Empty structs中,我们可以看见使用者采用变通方法,并意识到需要一个官方的解决方案。所以我们才在语言中追加了空结构体。

基于数据优化

在没有实际数据的前提下进行优化,充其量仅是无用优化,最坏情况下则是件危险的事情。当设计优化时(例如:性能,库大小),我们应当遵从基于数据的原则。

例如,在RFC-0032: Efficient envelopes 原本是被接受的,但是后来被拒绝。事后看来,是因为没有数据支持,它就不应该被接受。

避免连带影响(breakage at a distance)

我们应该努力避免连带影响。在一个地方的改变不应该造成另一个不相关的地方出现意外的损坏。例如,如果在一个FIDL文件中添加结构体 Foo 会破坏编译结果,令人惊讶的是原因居然是因为在一个完全不相关的地方的另一个FIDL文件中已经有相同名字 Foo 的结构体。这就是为什么FIDL像大多数的编程语言,使用命名空间来限制名称冲突的范围。

RFC-0029: Increasing method ordinals

因为与协议构成相关,所以讨论了这个问题

RFC-0048:Explicit union ordinals 重谈该话题,解释了为什么FIDL仅对协议使用散列法。

RFC-0057: Default no handles引入 value and resource types差异。这样做的一个动机是为了在Rust中提供Clone 特性用于没有句柄的类型,而不至于在其他地方发生破坏。

尽管FIDL绑定可以基于句柄的存在有条件地使能代码,但是这样做的后果不可预知的,因为这破坏了演化性保证。

例如,在列表中添加filed(字段)通常是安全的,但是添加句柄字段将破坏源—不仅对于列表,而且对包含该表的所有类型都是如此。

For example, FIDL accepts modifier keywords in any order, but we intend to enforce a consistent ordering in the linter.

As another example, RFC-0040: Identifier uniqueness fixed the problem of identifiers colliding after case transformation by having fidlc report an error if any two identifiers have the same canonical form. A simpler alternative would have been to enforce FIDL naming conventions in the compiler. However, this goes a step too far. There are valid reasons for using different naming styles, for example in describing the Kernel API, where snake_case methods are strongly preferred.

—->

自由语法,惯用风格

我们并不僵化坚持“用一种方式来做“的理念。我们担心用户会浪费时间来决定琐碎的选择,所以决定引入限制在 fidl-lint 或者 fidl-format 里,而不是 fidlc中。

例如,尽管FIDL接受任何顺序的修饰语关键字,但我们试图强制在linter中执行一致的顺序。

正如另一个例子中,RFC-0040: Identifier uniqueness 中修正了标识符冲突问题,即如果两个标识符拥有相同的规范形式,则经过大小写转换后fidlc会上报错误的异常。一个较为简单的选择则是在编译器中强制FIDL的命名惯例。尽管如此,这看起来还是太不合常理了。使用不同的命名风格还是存在合理的理由,例如在描述内核API时,我们强烈推荐snake_case命名法。

准则性表示法

FIDL线型格式是准则性的。对给定信息都有一个确切的编码对应。作为推论来说每一个字节都被计入:即没有一个字节可以在不改变信息含义的情况下被改变。

例如, specification规范定义中所有填充字节都为0。同样地,RFC-0047: Tables 不允许存储外来空包以此来确保准则性。

准则性表示法使FIDL更简单和安全。例如,允许填充非0可能因为这些信息正好占据了内存中的某些字节,造成FIDL泄露敏感信息。给定信息允许多种表现形式,同样也会造成很少运行的代码路径隐藏bug,例如,”外部空包“代码路径。准则性表示法同样在不知道模式的前提下能提供更简单的比较信息的平等性:对于value types,它是一个简单的 memcmp

不需要分配

wire format specification中:

FIDL被设计为可以在内存中就地完成信息的encoding (编码)和decoding (解码)

这个需求深远地影响了线型格式的设计:你仅使用堆就能完成就地解码。这也就是线型格式使用存在指示符和深度优先遍历顺序来代替的原因,例如当解码时,基于偏移的格式就需要附加数据结构来保持信息追踪。

当前原则与“You only pay for what you use”相关,当malloc 不存在时,它也可以适合FIDL底层使用,而不需要太大开销。

传输通用性

尽管有 库线型格式优先原则,但是考虑到其他重要的使用场景,例如描述内核API,进程内通信和持久化存储,这不表示FIDL应当与Zircon通道传输紧耦合。

RFC-0050: Syntax revamp 描述了传输通用化的长远方向。

RFC-0062: Method impossible 部分被拒绝,因为它让FIDL和Zircon通道传输耦合太紧密。