WSL WslcContainerState 容器状态枚举详解:状态语义、查询 API 与状态机实现
【免费下载链接】WSLWindows Subsystem for Linux项目地址: https://gitcode.com/GitHub_Trending/ws/WSL
WslcContainerState是 WSL(Windows Subsystem for Linux)容器 SDK(WslcSDK)中用于描述容器生命周期状态的 C 枚举类型,它贯穿容器从创建、启动、退出到删除的完整生命周期。本篇文章以官方 API 参考文档 WslcContainerState 为主体,结合仓库中 SDK 头文件、会话/容器实现与单元测试,为你讲清每个枚举值的语义、如何通过WslcGetContainerState查询状态,以及底层状态机是如何驱动状态迁移的。读完本文,你将能够在自己的 C/C++ 程序中正确判断容器状态,并理解状态在 WSL 服务端内部的流转机制。
1. 枚举定义与取值
在 WSL 的 C API(wslcsdk.h)中,容器状态通过如下枚举表达:
typedef enum WslcContainerState { WSLC_CONTAINER_STATE_INVALID = 0, WSLC_CONTAINER_STATE_CREATED = 1, WSLC_CONTAINER_STATE_RUNNING = 2, WSLC_CONTAINER_STATE_EXITED = 3, WSLC_CONTAINER_STATE_DELETED = 4, } WslcContainerState;各枚举值与语义对应如下:
| 枚举值 | 值 | 语义 |
|---|---|---|
WSLC_CONTAINER_STATE_INVALID | 0 | 无效/未知状态,通常用作查询前的初始化占位值,也用于标识未成功获取到的状态 |
WSLC_CONTAINER_STATE_CREATED | 1 | 容器已被创建但尚未启动(对应 Docker 的 created 状态) |
WSLC_CONTAINER_STATE_RUNNING | 2 | 容器正在运行,init 进程存活 |
WSLC_CONTAINER_STATE_EXITED | 3 | 容器已退出(正常退出或被停止) |
WSLC_CONTAINER_STATE_DELETED | 4 | 容器已被删除,对象即将失效 |
1.1 定义在仓库中的多处落点
这个枚举在仓库中并非只有一处,它同时存在于 SDK 公共头文件与跨进程共享的 IDL 定义中,保证 C API、C++/WinRT API 与 WSL 服务端三方使用同一套取值:
- C SDK 公共头:src/windows/WslcSDK/wslcsdk.h#L303-L310,这里同时声明了查询函数
WslcGetContainerState; - 服务端共享定义:src/windows/service/inc/WSLCShared.idl#L121-L128,使用
WslcContainerStateInvalid/Created/Running/Exited/Deleted命名,供 WSL 服务(wslservice)内部组件通过 MIDL 编译共享; - C++ API 层:见 doc/docs/api-reference/cpp/enumerations/containerstate.md,C++
Container::State()直接对WslcContainerState做强转(static_cast),底层取值保持一致(Invalid=0、Created=1、Running=2、Exited=3、Deleted=4)。
从源码结构看,这种"一处权威取值、多端共享"的设计,确保了 SDK 调用方与服务端记录的状态永远可以对齐,不会出现语义漂移。
2. 查询容器状态:WslcGetContainerState
枚举本身只是状态值的定义,真正把它用起来的是 SDK 提供的查询函数:
STDAPI WslcGetContainerState(_In_ WslcContainer container, _Out_ WslcContainerState* state);| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
container | WslcContainer | in | 由WslcCreateContainer或WslcOpenContainer等得到的容器句柄 |
state | WslcContainerState* | out | 接收查询结果的输出参数 |
返回值为HRESULT,S_OK表示成功。典型用法(见 WslcGetContainerState API 参考):
WslcContainerState state = WSLC_CONTAINER_STATE_INVALID; HRESULT hr = WslcGetContainerState(container, &state); if (SUCCEEDED(hr)) { // 根据 state 进行分支处理 }注意在调用前先将state初始化为WSLC_CONTAINER_STATE_INVALID,这是一个安全的防御性写法——即使调用失败,输出值也处于一个明确的"未知"占位状态,而不是未初始化的垃圾值。
在 COM 服务端,该查询最终落到WSLCContainer::GetState(src/windows/wslcsession/WSLCContainer.h#L319),并由WSLCContainerImpl::State()直接返回内部维护的m_state字段。
3. 实战:用状态机驱动容器生命周期
官方 端到端示例 给出了一个完整的生命周期流程:创建会话 → 拉取镜像 → 创建容器 → 启动容器 → 等待 init 进程退出 → 清理。其中在清理阶段就使用状态枚举做了"按状态决定行为"的判定:
// 6. Wait for the init process to exit WslcProcess initProc = nullptr; hr = WslcGetContainerInitProcess(container, &initProc); if (SUCCEEDED(hr)) { HANDLE exitEvent = nullptr; if (SUCCEEDED(WslcGetProcessExitEvent(initProc, &exitEvent))) { WaitForSingleObject(exitEvent, 30000); // 30-second timeout } INT32 exitCode = 0; if (SUCCEEDED(WslcGetProcessExitCode(initProc, &exitCode))) { printf("Process exited with code: %d\n", exitCode); } WslcReleaseProcess(initProc); } // 7. Clean up: 仅在容器仍处于 RUNNING 时才需要先 Stop WslcContainerState containerState = WSLC_CONTAINER_STATE_INVALID; if (SUCCEEDED(WslcGetContainerState(container, &containerState)) && containerState == WSLC_CONTAINER_STATE_RUNNING) { WslcStopContainer(container, WSLC_SIGNAL_SIGTERM, 10, nullptr); } WslcDeleteContainer(container, WSLC_DELETE_CONTAINER_FLAG_NONE, nullptr); WslcReleaseContainer(container); WslcTerminateSession(session); WslcReleaseSession(session);这段代码体现了一个关键工程实践:对已退出的容器重复调用Stop没有意义(服务端对此是 no-op),因此先用WslcGetContainerState判断状态,只有RUNNING才发送停止信号(WSLC_SIGNAL_SIGTERM,10 秒超时),随后无条件删除并释放资源。这种"先查状态、再执行操作"的模式,可以避免对容器做无效操作并降低竞态风险。
C++ 侧同样可以做到,官方 C++ 文档(containerstate.md)给出了强转比较的写法:
auto state = container.State(); if (state == static_cast<ContainerState>(2)) { // running }4. 底层状态机:状态如何迁移
理解枚举值只是第一步,真正值得深入的是服务端WSLCContainerImpl中维护m_state的状态机逻辑,它定义了哪些迁移是合法的、每个状态改变时系统会做什么。
4.1 CommitState:唯一的状态写入点
所有状态迁移都收敛在WSLCContainerImpl::CommitState(src/windows/wslcsession/WSLCContainer.cpp#L3108-L3129):
__requires_lock_held(m_lock) void WSLCContainerImpl::CommitState(WSLCContainerState State, std::int64_t Time, std::optional<int> ExitCode) noexcept { // N.B. A deleted container cannot transition back to any other state. WI_ASSERT(m_state != WslcContainerStateDeleted); WSL_LOG( "ContainerStateChange", TraceLoggingValue(static_cast<int>(m_state), "PreviousState"), TraceLoggingValue(static_cast<int>(State), "NewState"), TraceLoggingValue(m_id.c_str(), "ID")); m_state = State; m_stateGeneration++; m_stateChangedAt = Time; RecordEvent(WSLCStateToEventAction(State), Time, ExitCode); if (State == WslcContainerStateRunning) { // The restart's start phase landed, so a later exit must auto-delete an --rm container again. m_restart.reset(); } }关键设计点:
- 终态约束:代码通过断言(
WI_ASSERT)保证已DELETED的容器不允许再迁移回任何其他状态——DELETED是状态机的终结态; - 状态变更代际计数:每次迁移
m_stateGeneration++,配合m_stateChangedAt时间戳,用于并发场景下检测"状态是否发生了新一轮变化"(见 WSLCContainer.h#L278-L283 中的m_stateChangedAt、m_stateGeneration字段); - 事件记账:状态变更会调用
RecordEvent记录一条容器事件,action 由WSLCStateToEventAction(State)生成,事件带有容器 ID、时间戳与可选退出码(WSLCContainer.cpp#L1280-L1290),这些事件最终进入EventStore供 Docker 事件流等消费方使用; - 运行态副作用:当状态变为
RUNNING时,会清除正在进行的 restart 事务标记(m_restart.reset()),确保"先 stop 再 start"的重启事务不会跨越状态机边界。
4.2 事件驱动的迁移路径
容器状态并非由调用方直接"写死",而是由运行事件驱动迁移。核心入口是WSLCContainerImpl::OnEvent(WSLCContainer.cpp#L1293-L1339):
- Start 事件:当收到与当前 transition 预期一致的 Start 事件时,先断言当前状态为
CREATED或EXITED,然后CommitState(WslcContainerStateRunning, eventTime),即CREATED/EXITED → RUNNING; - Stop 事件:进入
OnStopped,最终在停止流程中CommitState(WslcContainerStateExited, stopTime, exitCode)(WSLCContainer.cpp#L1614),即RUNNING → EXITED,并携带退出码; - Destroy 事件:若当前状态不是
DELETED,则CommitState(WslcContainerStateDeleted, eventTime)并释放运行时资源(端口、挂载、进程等),随后通知等待 init 进程退出的阻塞方。
由此可以得到完整的合法状态迁移图:
INVALID ──(创建成功)──▶ CREATED ──(Start)──▶ RUNNING ──(Stop/退出)──▶ EXITED │ │ │ ▼ └────(Delete)──▶ DELETED(终态) CREATED ─────────(Delete)──────────▶ DELETED EXITED ──────────(Delete)──────────▶ DELETED此外,RUNNING状态还关联一个"活动保持"(activity hold)机制:UpdateActivityHoldLockHeld保证容器仅在RUNNING时持有活动引用,从而让会话的虚拟机在容器运行期间保持存活,防止空闲回收把正在运行的容器所在的 VM 关掉(WSLCContainer.h#L240-L242)。
4.3 会话级的清理
在会话层,WSLCSession会定期擦除已删除的容器条目:std::erase_if(m_containers, ... entry.second->State() == WslcContainerStateDeleted)(WSLCSession.cpp#L2498)。也就是说,DELETED不仅是对象内部的终态,也是容器从会话容器表中被移除的前置条件。
5. 测试验证:状态机的行为契约
仓库的单元测试对状态语义做了非常详尽的验证,是理解枚举行为契约的最好参考。以 test/windows/WSLCTests.cpp#L7499 的ContainerState测试为核心,再加上其他用例,可以归纳出以下被测试固化的行为:
- 启动后为 RUNNING:
WslcStartContainer成功返回后,container.State()必须等于WslcContainerStateRunning(如 WSLCTests.cpp#L1788、#L7537); - 停止/退出后为 EXITED:容器执行完 init 进程退出或
Stop后,状态迁移为WslcContainerStateExited(WSLCTests.cpp#L7565、#L7005); - 对 EXITED 容器 Stop 是 no-op:测试明确验证"对已退出容器调用 Stop() 不会改变状态,仍保持
WslcContainerStateExited"(WSLCTests.cpp#L7795-L7797)——这正是前面实战示例中先查询状态再决定是否 Stop 的依据; - Kill 后为 EXITED:
Kill用例验证容器被强杀后同样进入EXITED并在列表中表现为 exited(WSLCTests.cpp#L7725-L7729); - 创建后为 CREATED:某些流程(如创建后不立即启动)会验证状态为
WslcContainerStateCreated(WSLCTests.cpp#L6884、#L7826); - 列表语义:
expectContainerList辅助函数将"容器名 + 镜像 + 状态"三元组与docker ps风格列表对齐,确保每个状态都能正确暴露给上层查询(WSLCTests.cpp#L7501)。
这些测试从黑盒层面把枚举的每个取值都变成了可验证的行为契约,也让WslcGetContainerState的返回值有了明确预期。
6. 小结与使用建议
回到本文主题:WslcContainerState是一个只有 5 个取值的小枚举,但它在 WSL 容器 SDK 中扮演着"生命周期路标"的角色:
- 取值约定:
INVALID=0 / CREATED=1 / RUNNING=2 / EXITED=3 / DELETED=4,C 与 C++ API 取值完全一致; - 查询方式:通过
WslcGetContainerState(container, &state)获取,调用前建议把输出变量初始化为WSLC_CONTAINER_STATE_INVALID; - 状态机约束:
DELETED是终态不可逆;CREATED/EXITED → RUNNING → EXITED → DELETED是主路径,所有迁移统一走CommitState并在加锁下完成,同时会记录事件、递增代际计数; - 工程实践:执行 Stop/Delete 等破坏性操作前先查询状态,可避免对已退出容器做无效操作。
如果你要开发基于 WSL 容器的编排或管理工具,推荐进一步阅读 WslcGetContainerState API 参考、端到端示例 以及 C++ 侧的状态文档 ContainerState,并对照 WSLCTests.cpp 中的状态用例来校验自己的实现行为。
【免费下载链接】WSLWindows Subsystem for Linux项目地址: https://gitcode.com/GitHub_Trending/ws/WSL
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考