进程间通信 (IPC)¶
Firefox 桌面版是一个多进程桌面应用程序。需要检测的代码可能位于其任何进程中,因此 FOG 提供了执行此操作的功能。
设计¶
FOG 的 IPC 设计在 bug 1618253 中制定。
它围绕几个特定的概念展开
禁止非交换操作¶
由于我们无法很好地跨所有进程强加指标操作的规范顺序,因此 FOG 在某些情况下禁止非 交换 指标操作。
例如,从多个进程对计数器指标进行 Add()
操作可以正常工作,因为顺序无关紧要。但是,如果多个进程同时对字符串指标进行 Set()
操作,它应该采用哪个值?
这种歧义不是构建信任的良好基础,因此我们禁止从多个进程设置字符串指标。
禁止操作列表¶
布尔值的
set
(这是指标类型的唯一操作)带标签的布尔值的
set
(这是指标类型的唯一操作)字符串的
set
(这是指标类型的唯一操作)带标签的字符串的
set
(这是指标类型的唯一操作)字符串列表的
set
add
允许(不保证顺序和唯一性)
时间跨度的
start
、stop
和cancel
(这些是指标类型的唯一操作)UUID 的
set
和generateAndSet
(这些是指标类型的唯一操作)日期时间的
set
(这是指标类型的唯一操作)数量的
set
(这是指标类型的唯一操作)
随着新指标类型的添加,此列表可能会随着时间推移而增长。
安全阀:permit_non_commutative_operations_over_ipc
¶
如果您希望放弃 FOG 在排序方面的保护和保证,并在子进程中使用非交换操作,则可以使用 permit_non_commutative_operations_over_ipc
元数据属性标记指标定义,如下所示
unordered_category:
unordered_boolean_metric:
type: boolean
metadata:
permit_non_commutative_operations_over_ipc: true
...
目前仅支持
布尔指标
带标签的布尔指标
注意
如果此列表中没有您需要在非父进程中使用的指标类型,请访问 #glean 频道,我们将为您提供帮助。
进程无关性¶
对于可以在跨进程使用的指标类型,FOG 没有提供识别检测位于哪个进程中的功能。
这意味着,如果您在多个进程中累积到 时间分布,则所有进程的所有样本都将合并到同一指标中。
如果您希望区分来自不同进程类型的样本,则需要多个指标和内联代码来为给定进程选择合适的指标。例如
if (XRE_GetProcessType() == GeckoProcessType_Default) {
mozilla::glean::performance::cache_size.Accumulate(numBytes / 1024);
} else {
mozilla::glean::performance::non_main_process_cache_size.Accumulate(numBytes / 1024);
}
调度¶
FOG 不保证何时将非主进程指标值发送到 IPC。FOG 将尽最大努力在空闲时刻和有序关闭期间进行机会性调度。
在一些情况下,我们提供了更坚实的保证
测试¶
Rust、C++ 和 Javascript 中有一些仅限测试的 API。这些不会等待子进程指标值的刷新。您可以使用仅限测试的方法 testFlushAllChildren
在 FOG
XPCOM 组件上等待子进程数据的到达
await Services.fog.testFlushAllChildren();
Ping¶
我们不保证非主进程数据已进入特定 Ping。
内置 Ping 由 Rust Glean SDK 在 FOG 不直接控制的时间提交,因此在提交内置 Ping 时,父进程中可能不存在某些数据。我们预计这不会造成问题,因为“错过”给定 Ping 的子进程数据将包含在下一个 Ping 中。
目前,自定义 Ping 必须在父进程中发送,并且没有机制安排在子进程数据到达父进程后提交。 bug 1732118 跟踪此类机制或保证的添加。
关闭¶
在有序关闭期间,我们将尽最大努力刷新子进程中所有挂起的数据。这意味着无序关闭(通常是崩溃)可能导致子进程数据丢失。
大小¶
我们不会测量或保持 IPC 负载大小的最新计算。但是,我们确实保留了 IPC 负载访问次数的计数。这被用作 IPC 负载大小的(非常)保守估计,因此我们不会超过 IPC 消息大小限制。
请参阅 bug 1745660。
机制¶
大致的设计是,父进程可以请求立即刷新挂起的数据,每个子进程可以在任何时候决定刷新其挂起的数据。前者通过 FlushFOGData() returns (ByteBuf)
实现,后者通过 FOGData(ByteBuf)
实现。
挂起数据是由 Rust 中的 bincode
在子进程中生成的字节缓冲区,传递给 C++,通过 IPC 传递,然后返回到父进程中的 Rust 中的 bincode
。
然后,Rust 负责将挂起数据转换为父进程中指标的 指标 API 调用。
支持的进程类型¶
FOG 支持以下类型的子进程与父进程之间的消息传递
内容子进程(通过
PContent
(目前如此。请参阅 bug 1641989))gmp 子进程(通过
PGMP
)gpu 子进程(通过
PGPU
)rdd 子进程(通过
PRDD
)套接字子进程(通过
PSocketProcess
)实用程序子进程(通过
PUtilityProcess
)
有关这意味着什么的更多信息,请参阅 进程模型文档。
为新进程类型添加支持¶
为新进程类型添加支持是将上面“机制”中提到的两个消息扩展到另一个进程类型的协议(ipdl 文件)的问题。
在
P<ProcessType>.ipdl
的相应部分添加两个消息(( 注意:
PGPU
应该 是唯一一个parent
表示非父/主/UI 进程的 ipdl,但请仔细检查您是否正确理解了这一点。))将
async FOGData(ByteBuf&& aBuf);
添加到父/主/UI 进程端(通常是parent:
)。将
async FlushFOGData() returns (ByteBuf buf);
添加到非父/主/UI 端(通常是child:
)。
在
P<ProcessType>{Child|Parent}.{h|cpp}
中实现协议端点添加到
parent:
部分的消息位于P<ProcessType>Parent.{h|cpp}
中,反之亦然。
添加到
FOGIPC.cpp
的FlushAllChildData
代码,该代码枚举所有新支持类型的进程(可能只有一个),
在每个进程上调用 `SendFlushFOGData,以及
将结果 Promise 添加到数组中。
将正确的
GeckoProcessType_*
枚举值添加到FOGIPC.cpp
的SendFOGData
中,以及获取父进程单例并对其调用SendFOGData
的相应代码。将关闭时刷新 IPC 数据的处理添加到 fog crate 的
register_process_shutdown
函数中。如果未添加此项,我们将在不支持的进程类型上首次使用 Glean API 时记录(但不恐慌)。“处理”可能是一个带有注释的空块,解释在哪里可以找到它(例如
PROCESS_TYPE_DEFAULT
的处理方式)或者它可能是自定义代码(例如
PROCESS_TYPE_CONTENT
的)
将您添加支持的进程类型添加到已记录的 支持的进程类型列表 中。