进程间通信 (IPC)¶
Firefox 桌面版是一个多进程桌面应用程序。需要检测的代码可能位于其任何进程中,因此 FOG 提供了执行此操作的功能。
设计¶
FOG 的 IPC 设计在 bug 1618253 中制定。
它围绕几个特定的概念展开
禁止非交换操作¶
由于我们无法很好地跨所有进程强加指标操作的规范顺序,因此 FOG 在某些情况下禁止非 交换 指标操作。
例如,从多个进程对计数器指标进行 Add() 操作可以正常工作,因为顺序无关紧要。但是,如果多个进程同时对字符串指标进行 Set() 操作,它应该采用哪个值?
这种歧义不是构建信任的良好基础,因此我们禁止从多个进程设置字符串指标。
禁止操作列表¶
布尔值的
set(这是指标类型的唯一操作)带标签的布尔值的
set(这是指标类型的唯一操作)字符串的
set(这是指标类型的唯一操作)带标签的字符串的
set(这是指标类型的唯一操作)字符串列表的
setadd允许(不保证顺序和唯一性)
时间跨度的
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的)
将您添加支持的进程类型添加到已记录的 支持的进程类型列表 中。