ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

WSL 容器 SDK C++/WinRT 指南:用 ProcessCrashHandler 订阅 Session::ProcessCrashed 崩溃事件

WSL 容器 SDK C++/WinRT 指南:用 ProcessCrashHandler 订阅 Session::ProcessCrashed 崩溃事件 WSL 容器 SDK C/WinRT 指南用 ProcessCrashHandler 订阅 Session::ProcessCrashed 崩溃事件【免费下载链接】WSLWindows Subsystem for Linux项目地址: https://gitcode.com/GitHub_Trending/ws/WSL导读ProcessCrashHandler是微软 WSLWindows Subsystem for Linux容器 SDKWSLC SDK中用于响应 Linux 进程崩溃的核心委托类型当会话内运行的 Linux 进程因信号如 SIGSEGV而崩溃并生成崩溃转储crash dump时SDK 会通过Session::ProcessCrashed事件携带一个ProcessCrashInformation参数回调订阅者。本文以仓库文档 processcrashhandler.md 为骨架结合 Session.cpp、ProcessCrashInformation.cpp、wslcsdk.h 等源码与 WslcSdkWinRTTests.cpp 测试用例讲清如何订阅崩溃事件、事件携带哪些字段、底层如何桥接、如何验证与取消订阅帮助开发者把进程崩溃监控与崩溃转储收集能力集成进基于 WSL 容器 SDK 的宿主程序。ProcessCrashHandler崩溃事件委托的入口在 C/WinRT 的投影层中ProcessCrashHandler被定义为一个 WinRT 委托delegate。在 SDK 的 IDL 定义 wslcsdk.idl 中可以看到它的完整契约runtimeclass ProcessCrashInformation { // ... } delegate void ProcessCrashHandler(ProcessCrashInformation information);随后Session运行时类将该委托作为事件类型对外暴露event ProcessCrashHandler ProcessCrashed;也就是说Session::ProcessCrashed是标准的 WinRT 事件订阅它时需要提供一个类型为ProcessCrashHandler的回调回调的唯一参数是一个ProcessCrashInformation对象。其投影签名位于 Session.hwinrt::event_token ProcessCrashed(winrt::Microsoft::WSL::Containers::ProcessCrashHandler const handler); void ProcessCrashed(winrt::event_token const token) noexcept;重载的第二个版本用于按event_token取消订阅与 C/WinRT 的标准事件模式完全一致。订阅崩溃事件最小可用示例关联文档给出了订阅崩溃事件的最小示例这里完整保留并补充说明session.ProcessCrashed([](ProcessCrashInformation const info) { printf(process crashed: %ws\n, info.ProcessName().c_str()); });要点session是已通过Start()启动的Session对象Session的构造、Start()流程见 Session.cpp。事件在会话生命周期内有效建议在Start()之后、创建/启动容器之前完成订阅以免漏掉早期崩溃。回调在 SDK 内部线程池上下文中被触发详见下文底层链路不要在回调里执行长时间阻塞操作需要做重活时应把ProcessCrashInformation复制出来投递到自己的线程或队列。示例使用printf打印进程名实际工程中建议同时处理DumpPath()把崩溃转储归档到持久化存储。使用auto_revoke自动管理订阅在 C/WinRT 中更安全、更符合 RAII 习惯的写法是使用winrt::auto_revoke返回的 revoker它会在离开作用域时自动取消订阅。这一点也被仓库测试用例 WslcSdkWinRTTests.cpp 所采用auto revoker session.ProcessCrashed(winrt::auto_revoke, { // 保存 DumpPath、ProcessName、Pid、Signal、Timestamp std::wprintf(Lcrash: %s pid%u signal%u dump%s\n, info.ProcessName().c_str(), info.Pid(), info.Signal(), info.DumpPath().c_str()); });若订阅时传入普通 lambda则返回值是winrt::event_token需要手动调用session.ProcessCrashed(token)来退订。ProcessCrashInformation崩溃事件携带的字段当事件被触发时回调收到的ProcessCrashInformation对象封装了一次进程崩溃的全部关键信息。其接口定义在 ProcessCrashInformation.h属性类型含义DumpPath()winrt::hstring本次崩溃生成的转储文件完整路径Windows 路径ProcessName()winrt::hstring发生崩溃的 Linux 进程名Pid()uint32_t崩溃进程在容器/PID 命名空间内的进程 IDSignal()uint32_t导致崩溃的 Linux 信号编号如 11 表示 SIGSEGVTimestamp()winrt::Windows::Foundation::DateTime崩溃发生的时间戳这些字段并非凭空生成而是由底层 C API 的WslcSessionCrashDumpInfo结构体逐字段映射而来映射逻辑见 ProcessCrashInformation.cppProcessCrashInformation::ProcessCrashInformation(const WslcSessionCrashDumpInfo* info) { m_dumpPath info-dumpPath; m_processName winrt::to_hstring(info-processName); m_pid info-pid; m_signal info-signal; m_timestamp winrt::clock::from_time_t(static_casttime_t(info-timestamp)); }对应的 C 结构体定义在 wslcsdk.htypedef struct WslcSessionCrashDumpInfo { _Field_z_ PCWSTR dumpPath; // 崩溃转储文件的宽字符路径 _Field_z_ PCSTR processName; // 进程名ANSI uint32_t pid; // 进程 ID uint32_t signal; // 导致崩溃的信号 uint64_t timestamp; // 时间戳Unix 秒 } WslcSessionCrashDumpInfo;注意timestamp是 Unix 秒级时间戳投影层用winrt::clock::from_time_t将其转换为DateTime由于是秒级粒度比较时间时需要容忍秒级误差测试中对此有专门说明见下文。底层链路从 Linux 崩溃到 WinRT 事件的完整桥接Session::ProcessCrashed事件并不是直接由 WinRT 对象自发生成的它跨越了C API → COM 回调 → WinRT 事件三层。理解这条链路有助于排查事件没触发的问题。第一层C API 注册崩溃回调Session::Start()在成功创建会话后立即注册崩溃转储回调Session.cpphr WslcRegisterSessionCrashDumpCallback(m_session.get(), Session::OnCrashDump, this, m_crashDumpSubscription, errorMessage.put());WslcRegisterSessionCrashDumpCallback的声明位于 wslcsdk.h签名要点STDAPI WslcRegisterSessionCrashDumpCallback( _In_ WslcSession session, _In_ WslcSessionCrashDumpCallback crashDumpCallback, // C 风格回调 _In_opt_ PVOID crashDumpContext, // 用户上下文 _Out_ WslcCrashDumpSubscription* subscription, // 订阅句柄保持存活 _Outptr_opt_result_z_ PWSTR* errorMessage);头文件注释明确了两点关键语义wslcsdk.h该回调适用于持有存活会话的任何调用者即谁持有 session 句柄谁就可以注册返回的WslcCrashDumpSubscription订阅句柄会保持注册存活释放它WslcReleaseCrashDumpSubscription即取消订阅同一个会话可以注册多个订阅。第二层COM 接口桥接到 C 风格回调C API 内部通过CrashDumpCallback这个 WinRT COM 实现类IWSLCCompatCrashDumpCallback把服务端的 COM 回调转译成 C 风格回调。见 CrashDumpCallback.cppHRESULT STDMETHODCALLTYPE CrashDumpCallback::OnCrashDump( _In_ LPCWSTR DumpPath, _In_opt_ LPCSTR ProcessName, _In_ ULONG Pid, _In_ ULONG Signal, _In_ ULONGLONG Timestamp) try { if (m_callback) { WslcSessionCrashDumpInfo info{}; info.dumpPath DumpPath; info.processName ProcessName ? ProcessName : ; info.pid Pid; info.signal Signal; info.timestamp Timestamp; m_callback(info, m_context); // 转交用户注册的 C 回调 } return S_OK; } CATCH_RETURN();可以看到COM 接口传入的五个参数转储路径、进程名、PID、信号、时间戳在此被组装成WslcSessionCrashDumpInfo再交给用户在WslcRegisterSessionCrashDumpCallback中注册的回调。第三层静态回调包装并触发 WinRT 事件WinRT 投影层的Session::OnCrashDump静态回调注册时作为 C 回调传入收到WslcSessionCrashDumpInfo后先构造ProcessCrashInformation包装对象再触发m_crashDumpEvent事件Session.cppvoid CALLBACK Session::OnCrashDump(const WslcSessionCrashDumpInfo* info, PVOID context) noexcept { try { auto session static_castSession*(context); auto information winrt::make_selfimplementation::ProcessCrashInformation(info); session-m_crashDumpEvent(*information); // 同步调用所有已订阅的 handler } CATCH_LOG(); }事件存储与订阅句柄的声明在 Session.hwinrt::eventwinrt::Microsoft::WSL::Containers::ProcessCrashHandler m_crashDumpEvent; wil::unique_anyWslcCrashDumpSubscription, decltype(WslcReleaseCrashDumpSubscription), WslcReleaseCrashDumpSubscription m_crashDumpSubscription;当Session::Close()被调用时m_crashDumpSubscription.reset()会释放订阅句柄、注销回调Session.cpp即便用户忘记显式调用Close()final_release也会在引用计数归零时兜底清理Session.cpp。崩溃转储的产生前提值得注意并非所有进程死掉都会触发该事件触发前提是系统为该崩溃写入了崩溃转储。测试用例 WslcSdkWinRTTests.cpp 中有一条重要注释不能用容器内 PID 1 的 init 进程来制造崩溃因为 Linux 会静默丢弃针对 PID 1 且采用默认处置的kill()信号导致不会生成 core dump因此测试通过exec启动一个普通子进程再向其发送 SIGSEGV 来验证。开发者在设计崩溃注入/验证实验时也应遵循这一约束。测试验证仓库如何证明该事件可用仓库提供了从 WinRT 事件层到 C API 层的完整测试证据可作为功能契约参考。WinRT 事件层测试WslcSdkWinRTTests.cpp 的ProcessCrashedEvent用例完整覆盖了正反两个场景正向创建容器后订阅ProcessCrashedexec一个执行kill -SEGV $$的/bin/sh进程。断言DumpPath()非空且文件真实存在、ProcessName()包含 sh、Pid() 0、Signal() 11SIGSEGV且时间戳落在崩溃前后 1 秒的容差窗口内因为时间戳为秒级粒度。反向取消订阅让 revoker 先析构自动退订再触发同样的崩溃等待后断言回调计数仍为 0证明退订确实生效。此外ProcessCrashInformationProperties用例WslcSdkWinRTTests.cpp直接构造WslcSessionCrashDumpInfo验证ProcessCrashInformation五个属性与底层结构体字段的一一映射关系。C API 层测试WslcSdkTests.cpp 的CrashDumpCallback用例验证WslcRegisterSessionCrashDumpCallback的注册、回调触发与WslcReleaseCrashDumpSubscription释放WSLCTests.cpp 的CrashDumpCallback用例从IWSLCSession::RegisterCrashDumpCallback的角度验证 COM 层回调WSLCTests.cpp 与 UnitTests.cpp 还覆盖了崩溃转储目录、maxCrashDumpCount等配置对转储收集行为的影响说明转储文件的落盘位置与数量上限是可配置的。实战建议与常见误区综合文档、源码与测试给出以下可落地的建议订阅时机在Session::Start()之后立即订阅ProcessCrashed避免容器内进程先于订阅发生崩溃而丢失事件。回调线程模型事件在 SDK 内部线程上同步分发m_crashDumpEvent(*information)回调内避免阻塞需要时拷贝字段到自己的队列异步处理。转储归档优先消费DumpPath()将转储文件移动/复制到长期存储并同时记录Signal()、Pid()、Timestamp()形成崩溃台账注意回调触发后转储文件可能随时被后续清理策略覆盖越早归档越安全。订阅生命周期优先使用winrt::auto_revokerevoker 或event_token手动退订确保 handler 与Session的生命周期匹配防止悬垂引用Close()或引用计数归零后订阅自动注销。信号语义Signal()是 Linux 信号编号如 11 SIGSEGV、6 SIGABRT可用作崩溃类型分类但 PID 1 进程的默认信号处置不会产生 core dump监控 init 进程异常时应另行依赖Terminated事件与会话终止原因WSLC_SESSION_TERMINATION_REASON_CRASHED见 wslcsdk.h。总结ProcessCrashHandler是 WSL 容器 SDK 暴露进程崩溃事件的唯一入口配合Session::ProcessCrashed与ProcessCrashInformation开发者只需几行代码即可获得进程名 PID 信号 时间戳 转储路径的完整崩溃画像。其实现横跨WslcRegisterSessionCrashDumpCallbackC API、IWSLCCompatCrashDumpCallbackCOM 桥接与 WinRT 事件三层并通过正反双向测试用例验证了触发与退订语义。无论是构建崩溃监控看板、自动收集转储做离线分析还是实现崩溃自愈重启逻辑本文所述的事件订阅模式都是可靠的起点。【免费下载链接】WSLWindows Subsystem for Linux项目地址: https://gitcode.com/GitHub_Trending/ws/WSL创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表