WslcTerminateSession 详解:WSL C API 中 WSLC 容器会话的终止机制、返回码语义与资源释放顺序
WslcTerminateSession 详解WSL C API 中 WSLC 容器会话的终止机制、返回码语义与资源释放顺序【免费下载链接】WSLWindows Subsystem for Linux项目地址: https://gitcode.com/GitHub_Trending/ws/WSL本文聚焦 WSL 仓库中 WSLCWindows Subsystem for Linux ContainersC API 的WslcTerminateSession函数完整覆盖其函数签名、参数与返回码、在会话生命周期中的位置与调用顺序并结合 wslcsdk.cpp 与 WSLCSession.cpp 的源码剖析其终止流程中的幂等性保证、IO/COM 回调取消与运行时关闭过程。读完后你将能够正确地在宿主应用中终止 WSLC 会话、处理可能的错误码并理解“Terminate 之后为什么还必须 Release”这一常见的资源管理问题。1. API 概览签名、参数与返回值WslcTerminateSession的完整声明摘自 wslcterminatesession.md如下STDAPI WslcTerminateSession(_In_ WslcSession session);ParameterTypeDirectionsessionWslcSessionin参数session由 WslcCreateSession 创建并返回的会话句柄不透明指针类型WslcSession。返回值HRESULT。成功返回S_OK失败时返回描述原因的 HRESULT见第 3 节的错误语义。官方示例原文档给出的最小调用HRESULT hr WslcTerminateSession(session);该函数在 WSLC SDK 中的导出声明位于 wslcsdk.hSTDAPI WslcTerminateSession(_In_ WslcSession session);其声明与实现同属 Session APIs 家族完整成员列表可参见 Session APIs 索引包括创建WslcCreateSession、终止事件查询WslcGetSessionTerminationEvent/WslcGetSessionTerminationReason、崩溃转储回调注册以及释放WslcReleaseSession等 14 个函数。2. 在会话生命周期中的位置先 Terminate再 Release理解WslcTerminateSession的关键是把它放回完整生命周期WslcInitSessionSettings初始化会话设置对象WslcSetSessionSettingsCpuCount/WslcSetSessionSettingsMemory/WslcSetSessionSettingsTimeout/WslcSetSessionSettingsVhd/WslcSetSessionSettingsFeatureFlags配置 VM 资源与特性WslcCreateSession创建会话并启动底层运行时使用会话进程启动、IO 中继、网络等WslcTerminateSession终止会话关闭底层 VM 运行时WslcReleaseSession释放 SDK 包装对象本身。仓库中的完整示例 WSLC-HelloWorld 展示了标准的收尾顺序helloworld.c#L207-L208WslcTerminateSession(session); WslcReleaseSession(session);这两步的职责不同WslcTerminateSession终止的是会话所代表的底层运行时WSLC 容器 VM而WslcReleaseSession释放的是SDK 内部的包装对象。即使会话已经自行结束例如进程退出、触发超时或异常终止WslcReleaseSession依然必须调用以回收句柄对应的包装对象同样地WslcTerminateSession调用失败也不会阻止你后续执行 Release。C API 端到端示例 中也遵循同样的“Terminate Release”收尾模式。3. SDK 层实现参数校验与错误码SDK 导出函数的实现非常精简位于 wslcsdk.cpp#L454-L462STDAPI WslcTerminateSession(_In_ WslcSession session) try { auto internalType CheckAndGetInternalType(session); RETURN_HR_IF_NULL(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), internalType-session); RETURN_HR(internalType-session-Terminate()); } CATCH_RETURN();逐行解读CheckAndGetInternalType(session)把不透明的WslcSession指针还原为 SDK 内部的WslcSessionImpl结构。该函数是所有 Session API 共用的入口校验session为nullptr或类型不合法时直接返回失败。RETURN_HR_IF_NULL(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), internalType-session)若会话内部指针为空例如会话对象尚未完成创建、或底层对象已经不存在返回HRESULT_FROM_WIN32(ERROR_INVALID_STATE)即0x80070396。这是调用方最常见的一种失败返回通常意味着对无效/已失效的句柄调用了 Terminate。internalType-session-Terminate()真正的终止逻辑委托给服务端的IWSLCSession::Terminate()其 HRESULT 原样透传给调用方。try/CATCH_RETURN()所有异常被转换为 E_FAIL 类 HRESULT保证 C ABI 边界不会抛出异常。因此调用方最稳健的写法是检查返回值是否SUCCEEDED失败时读取 HRESULT无论成败与否都继续执行WslcReleaseSession。4. 服务端终止流程幂等、取消 IO、释放运行时WslcTerminateSession最终落到 WSLCSession::Terminate()。这段代码约 70 行是整个 WSLC 会话生命周期里并发处理最密集的函数之一值得逐段拆解。4.1 幂等性m_terminating.exchange(true)if (m_terminating.exchange(true)) { return S_OK; }m_terminating是一个原子标志。exchange(true)在把标志置为true的同时返回旧值如果旧值已经是true说明另一个线程已经在执行终止流程本次调用直接返回S_OK。这带来两个重要语义多次调用是安全的幂等宿主应用、SYSTEM 服务、空闲超时机制同时触发终止时不会重复执行关闭流程源码注释明确指出这个检查必须在获取运行时独占锁之前完成。因为OnVmExited()会从 IORelay 线程回调进来如果外部Terminate()持有独占锁并调用m_runtime.Relay()-Stop()relay 线程再重入Terminate()就会在该锁上死锁。4.2 带重试的独占锁获取取消 IO 与 COM 回调wil::rwlock_release_exclusive_scope_exit sessionLock; bool retrying false; while (!sessionLock) { if (retrying) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); } { std::lock_guard lock(m_userHandlesLock); if (!m_sessionTerminatingEvent.is_signaled()) { m_sessionTerminatingEvent.SetEvent(); m_eventStore.OnSessionTerminating(); } CancelUserHandleIO(); } { std::lock_guard comLock(m_userCOMCallbacksLock); CancelUserCOMCallbacks(); } sessionLock m_runtime.TryLockExclusive(); retrying true; }这里解决的问题是CancelIoEx()无法与正在进行的同步ReadFile()精确同步——两次ReadFile()之间调用CancelIoEx()不会有任何效果因此无法靠“取消一次 IO”就保证没有其他线程卡住。源码的对策是一个**“取消 重试取锁”循环**设置m_sessionTerminatingEvent仅在未置位时执行一次这是一个全局可用的终止通知事件。会话内部所有可能阻塞较久的操作如WaitForEventOrSessionTerminatingWSLCSession.cpp#L3400-L3417都会把它与自己的等待事件一起做WaitForMultipleObjects一旦该事件被置位等待方要么以E_ABORT“Session %lu is terminating.”放弃操作要么被m_eventStore.OnSessionTerminating()唤醒并中断事件流读取避免“等一个永远不会完成的操作”CancelUserHandleIO()取消挂在用户提供的句柄上的挂起 IO针对不支持 overlapped IO 的句柄场景解除同步 IO 阻塞CancelUserCOMCallbacks()取消正在等待跨进程 COM 响应的出站回调例如IProgressCallback::OnProgress解除等待宿主应用回包造成的阻塞m_runtime.TryLockExclusive()尝试以非阻塞方式获取运行时独占锁失败则休眠 10ms 后回到步骤 1直到取得锁为止。由于每一步都在主动解除“持锁线程的阻塞源”这个循环最终必然收敛。4.3 关闭运行时与撤销全局接口m_runtime.Shutdown(sessionLock, m_terminationReason, m_terminationDetails); if (m_vmFactoryGitCookie ! 0) { LOG_IF_FAILED(m_git-RevokeInterfaceFromGlobal(m_vmFactoryGitCookie)); m_vmFactoryGitCookie 0; } return S_OK;m_runtime.Shutdown(...)在已持有的独占锁上执行运行时关闭并写入m_terminationReason/m_terminationDetails。这两个字段正是后续 WslcGetSessionTerminationReason 所暴露的数据来源——主动 Terminate 与 VM 自行退出会留下不同的终止原因m_git-RevokeInterfaceFromGlobal(...)撤销从全局接口表GIT中登记的 VM 工厂。源码注释解释既然空闲拆除已禁用且终止后不再有任何操作可以运行被“停放”的 VM 工厂无法再被重新获取因此必须显式注销函数总是返回S_OK除非在锁循环中抛出异常经CATCH_RETURN转码终止一旦开始即被视为成功受理。5. 服务端的弱引用终止路径除 SDK 直接调用外WSL 服务SYSTEM 权限的 wslservice还需要一种“不受会话状态影响”的终止手段用于特权调用方如管理员强制结束请求。这由 WSLCSessionReference 实现HRESULT wslc::WSLCSessionReference::Terminate() { // Resolve the weak reference directly (bypassing OpenSession which checks GetState). // We want to terminate regardless of session state. Microsoft::WRL::ComPtrIWSLCSession session; RETURN_IF_FAILED(m_weakSession-Resolve(__uuidof(IWSLCSession), ...)); if (session) { return session-Terminate(); } return S_OK; // Session already released }对比两条路径可以看出设计上的细微差别见 WSLCSessionReference.cpp#L34-L68OpenSession会先Resolve弱引用再检查GetState()必须为WSLCSessionStateRunning否则返回ERROR_OBJECT_NO_LONGER_EXISTS/ERROR_INVALID_STATE——用于“会话还活着吗”的探测Terminate刻意绕过状态检查直接Resolve无论会话处于什么状态运行中、正在退出、已空闲都尝试终止若弱引用解析为空会话对象已被完全释放返回S_OK因为目标已达成。从源码结构看两条路径最终汇聚到同一个WSLCSession::Terminate()其m_terminating原子标志保证了无论多少条路径并发触发关闭流程只执行一次。6. 实践要点与错误处理结合上述实现宿主应用集成WslcTerminateSession时建议遵循以下准则收尾顺序固定WslcTerminateSession(session)→WslcReleaseSession(session)与 WSLC-HelloWorld 示例 保持一致Terminate 失败不阻止 Release。对返回值做SUCCEEDED判断典型失败是0x80070396HRESULT_FROM_WIN32(ERROR_INVALID_STATE)对应句柄无效或内部会话对象缺失wslcsdk.cpp#L458其余失败码来自服务端终止流程的异常转码。重复调用是安全的由于幂等标志的存在无需在调用侧再维护“是否已终止”的本地状态但应理解多次调用中只有第一次真正执行关闭。终止是“受理”语义WslcTerminateSession返回S_OK表示终止流程已受理/完成受理会话状态与终止原因可随后通过WslcGetSessionTerminationEvent等待终止完成和WslcGetSessionTerminationReason读取原因进一步确认。适用前提本 API 属于 WSLC C APIwslcsdk.dll导出声明见 wslcsdk.def运行于 Windows 宿主session句柄必须先由WslcCreateSession成功取得。对 WSL 常规 Linux 发行版非 WSLC 容器会话不直接适用本函数。7. 相关源码与文档索引内容路径API 官方文档本文主体wslcterminatesession.mdSession APIs 全列表session-apis/index.mdSDK 头文件声明wslcsdk.h#L164SDK 导出实现wslcsdk.cpp#L454-L462服务端终止核心逻辑WSLCSession.cpp#L3419-L3487终止等待与事件竞争处理WSLCSession.cpp#L3400-L3417服务侧弱引用终止WSLCSessionReference.cpp#L54-L68可运行示例WSLC-HelloWorld/helloworld.c端到端 C API 示例end-to-end-example.mdWSLC 测试含终止场景test/windows/WslcSdkTests.cpp通过以上阅读路径你可以从一句WslcTerminateSession(session)的调用一路追踪到 SDK 参数校验、服务端幂等终止、IO/COM 取消与运行时关闭的完整链路从而在自有宿主程序中以正确的方式管理 WSLC 容器会话的整个生命周期。【免费下载链接】WSLWindows Subsystem for Linux项目地址: https://gitcode.com/GitHub_Trending/ws/WSL创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考