| RFC-0074:公开进程启动时间 | |
|---|---|
| 状态 | 已接受 |
| 区域 |
|
| 说明 | 使进程启动时间可从用户空间查询。 |
| Gerrit 更改 | |
| 作者 | |
| 审核人 | |
| 提交日期(年-月-日) | 2021-02-19 |
| 审核日期(年-月-日) | 2021-03-11 |
摘要
我们重构了 ZX_INFO_PROCESS 以公开进程的启动时间,并清理了描述其运行时的标志。如果进程具有有效的开始时间,则表示存在 ZX_INFO_PROCESS_FLAG_STARTED 标志,并且该标志定义为调用 zx_process_start() 时的单调时间(如果有效)。
设计初衷
此功能最初在 crbug.com/726484 和 https://fxbug.dev/42105649 中提出,Chromium 请求此功能是为了为跟踪事件提供基本时间,从而扩展已支持 Linux、macOS 和 Windows 的平台无关接口。此外,我们还提交了 https://fxbug.dev/42119376,以利用此功能在崩溃报告中包含正常运行时间。
设计
在 ProcessDispatcher 的一个简单直接的扩展中,我们会在 ProcessDispatcher::AddInitializedThread() 中记录初始线程的时间,然后将其传递到 ProcessDispatcher::GetInfo() 中;前者是 zx_process_start() 的主要“启动”例程,而后者是 zx_get_object_info() 的例程,用于处理具有 ZX_INFO_PROCESS 的进程句柄。
至于由 zx_object_get_info() 填充的结构体,目前我们有:
typedef struct zx_info_process {
// The process's return code; only valid if |exited| is true.
// If the process was killed, it will be one of the ZX_TASK_RETCODE values.
int64_t return_code;
// True if the process has ever left the initial creation state,
// even if it has exited as well.
bool started;
// If true, the process has exited and |return_code| is valid.
bool exited;
// True if a debugger is attached to the process.
bool debugger_attached;
uint8_t padding1[5];
} zx_info_process_t;
我们会将其改进为:
typedef struct zx_info_process {
// The process's return code; only valid if the
// |ZX_PROCESS_INFO_FLAG_EXITED| flag is set. If the process was killed, it
// will be one of the |ZX_TASK_RETCODE| values.
int64_t return_code;
// The monotonic time at which zx_process_start() was called, only valid
// if the |ZX_INFO_PROCESS_FLAG_STARTED| flag is set.
zx_time_t start_time;
// Bitwise OR of ZX_INFO_PROCESS_FLAG_* values.
uint32_t flags;
uint8_t padding1[4];
} zx_info_process_t;
为此引入了以下标志值:
// Whether the process has started. zx_process_info_t::start_time is only
// valid if this flag is set.
#define ZX_INFO_PROCESS_FLAG_STARTED (1u << 0)
// Whether the process has exited.
#define ZX_INFO_PROCESS_FLAG_EXITED (1u << 1)
// Whether a debugger is attached to the process.
#define ZX_INFO_PROCESS_FLAG_DEBUGGER_ATTACHED (1u << 2)
将布尔值重构为标志并非严格必要,但这样做可使 zx_info_process_t 符合当前的系统调用结构政策,节省一个字节的填充空间,并且不会增加执行此提案所需的工作量(还可以避免下一位工程师遇到相当多的麻烦)。
实现
将旧结构体重命名为
zx_info_process_v1_t,并创建一个别名以通过旧名称 (zx_info_process_t->zx_info_process_v1_t) 引用它。将主题ZX_INFO_PROCESS重命名为ZX_INFO_PROCESS_V1,并创建一个指向旧名称 (ZX_INFO_PROCESS->ZX_INFO_PROCESS_V1) 的别名。添加新的结构 (
zx_info_process_v2_t) 和主题 (ZX_INFO_PROCESS_V2)。更改内核以跟踪进程启动时间并识别新的主题和结构。更新所有代码(树内和树外)以使用
ZX_INFO_PROCESS_V2。更改
ZX_INFO_PROCESS和zx_info_process_t别名,以引用 v2 主题和结构。等待之前的更改完全推出。
更新所有代码(树内和树外)以再次使用
ZX_INFO_PROCESS。移除 v1 主题和结构,以及 v2 别名。
这些类型的 Rust 和 Go 版本也会相应更新。
性能
添加的逻辑应产生可忽略不计的运行时费用,尤其是在单个进程的生命周期内摊销时。
安全注意事项
如果程序已获得进程的句柄,则可以执行和获取比进程启动时间更多的信息。此外,许多其他操作系统已经公开了启动时间,但似乎并未引起关注。
隐私注意事项
请参阅上文中的安全注意事项。
测试
Zircon 的进程相关核心测试将得到扩展,以断言(例如)在 zx_process_start() 之前和之后采集的时间样本确实夹在记录的开始时间之间,并且尚未启动的进程的 ZX_INFO_PROCESS_FLAG_STARTED 未设置。
文档
ZX_INFO_PROCESS 文档将相应更新。
缺点、替代方案和未知因素
还考虑了将“开始”时间作为更通用的任务级概念(例如,在 ZX_INFO_TASK_RUNTIME 下公开)公开。对于线程,情况相同:线程的开始时间是调用 zx_thread_start() 的时间点。对于作业,最自然的说法是,当其第一个子作业开始时,该作业“开始”。不过,由于作业可以嵌套,这会引入遍历任务子树,与处理其他两种任务类型相比,这种逻辑相对更复杂。最终,我们决定不使用扩展程序,因为它们无法满足当前的需求,并且会让我们避开任务层次结构遍历的需求。
在先技术和参考资料
Linux:/proc/[pid]/stat 会公开 starttime。
FreeBSD:/proc/[pid]/status 在以空格分隔的统计信息列表中公开了开始时间。
macOS:proc_pidinfo() 系统调用似乎公开了此信息。