WSL 容器 SDK(Microsoft.WSL.Containers)SessionSettings 完全指南:会话配置、校验规则与源码级原理
2026/9/10 22:32:24 网站建设 项目流程

WSL 容器 SDK(Microsoft.WSL.Containers)SessionSettings 完全指南:会话配置、校验规则与源码级原理

【免费下载链接】WSLWindows Subsystem for Linux项目地址: https://gitcode.com/GitHub_Trending/ws/WSL

SessionSettings 是 WSL(Windows Subsystem for Linux)容器 SDK(WinRT 命名空间Microsoft.WSL.Containers,底层为 WSLC/WSL Container 引擎)中用于描述一个容器会话(Session)资源配置的配置类。本文以 sessionsettings.md 为骨架,结合仓库内 WinRT 投影实现 SessionSettings.cpp、接口定义 wslcsdk.idl 与 C API 头文件 wslcsdk.h,系统讲解该类的构造约束、全部属性及其校验规则、底层桥接机制,并给出可直接运行的完整实战示例。读完本文,你将掌握如何正确创建、配置并校验一个 WSLC 会话,理解“设置对象一旦物化底层 C 结构后即不可变”的语义,并能独立编写基于 Session 的容器编排程序。

一、SessionSettings 在 WSLC SDK 中的定位

WSLC SDK 的 C++ 投影暴露在命名空间winrt::Microsoft::WSL::Containers下,整个对象模型分为几组(见 cpp API 索引):

  • 设置类(Settings Classes)SessionSettingsContainerSettingsProcessSettingsVhdOptions
  • 核心类(Core Classes)SessionContainerProcess
  • 数据类、枚举、委托与事件:如ImageInfoImageProgressContainerStateSessionTerminationHandler等。

其中SessionSettings是会话的“入口配置”:它决定了会话的名称、存储位置、CPU/内存配额、超时时间、VHD 盘要求以及是否启用 GPU。只有先把SessionSettings配好,才能构造 Session 并Start()一个 WSLC 容器会话。

在 wslcsdk.idl 中,该类型被声明为:

runtimeclass SessionSettings { SessionSettings(String name, String storagePath); String Name; String StoragePath; Windows.Foundation.IReference<UInt32> CpuCount; Windows.Foundation.IReference<UInt32> MemorySizeInMB; Windows.Foundation.IReference<Windows.Foundation.TimeSpan> Timeout; VhdOptions VhdRequirements; Boolean EnableGpu; };

注意CpuCountMemorySizeInMBTimeout都被声明为Windows.Foundation.IReference<T>(可空引用类型),这意味着它们是可选配置——不赋值时底层保持nullptr,交给 WSLC 服务端使用默认值;而NameStoragePathEnableGpu是强类型必填/默认值属性。

二、构造函数:会话名称与存储路径

SessionSettings的构造函数签名如下:

SessionSettings(hstring name, hstring storagePath);

两个参数都必须非空(empty 即抛异常):

  • name:要创建的会话名称;
  • storagePath:会话存储(session storage)写入的路径。如果该路径不存在,会被自动创建

在 SessionSettings.cpp 中,构造函数直接执行了校验:

SessionSettings::SessionSettings(hstring const& name, hstring const& storagePath) : m_name(name), m_storagePath(storagePath) { if (name.empty()) { throw winrt::hresult_invalid_argument(L"Session name cannot be empty"); } if (storagePath.empty()) { throw winrt::hresult_invalid_argument(L"Storage path cannot be empty"); } }

命名规则的三个关键事实

  1. 名称兼具展示与标识双重身份:会话名称既作为显示名称,也作为机器级(machine-wide)的键用于标识会话。
  2. 重名冲突:如果已存在同名会话,创建会失败并返回ERROR_ALREADY_EXISTS(对应 Win32 错误码 183)。
  3. 机器级可见性(安全警示):以下信息对机器上的所有用户可见:
    • 会话的名称(the session's name)
    • 创建该会话的用户的 SID
    • 创建该会话的进程的 PID

因此官方文档明确警告:不要在会话名称中放入凭据(credentials)或其他敏感信息。这一点在 C API 的错误码体系中也有呼应——wslcsdk.idl 定义了Error::InvalidSessionName = 0x80040608Error::SessionReserved = 0x80040607,会话名称的合法性由底层服务统一校验。

三、属性全景与校验规则

SessionSettings共暴露 7 个可读写属性。每个 setter 在 SessionSettings.cpp 中都有对应的参数校验,下表是完整清单:

属性类型必填/可选setter 校验规则(违规即抛hresult_invalid_argument
Name()hstring必填不能为空
StoragePath()hstring必填不能为空
CpuCount()IReference<uint32_t>可选拒绝0
MemorySizeInMB()IReference<uint32_t>可选拒绝0
Timeout()IReference<TimeSpan>可选不能为 0、不能为负、换算为毫秒后必须能放进uint32_t
VhdRequirements()VhdOptions可选拒绝nullptr(抛E_POINTER
EnableGpu()bool默认 false

下面逐条结合源码展开。

3.1 Name 与 StoragePath

setter 同样执行非空校验,并额外增加了“初始化后不可改”的保护:

void SessionSettings::Name(hstring const& value) { if (m_sessionSettings) { throw hresult_illegal_state_change(L"Cannot change session name after session has been initialized"); } if (value.empty()) { throw winrt::hresult_invalid_argument(L"Session name cannot be empty"); } m_name = value; }

StoragePath的 setter 逻辑完全对称(SessionSettings.cpp)。这里出现的m_sessionSettings成员就是物化后的底层 C 结构指针std::unique_ptr<WslcSessionSettings>,见 SessionSettings.h)。

3.2 CpuCount 与 MemorySizeInMB

二者都是IReference<uint32_t>0被显式拒绝:

void SessionSettings::CpuCount(IReference<uint32_t> const& value) { if (m_sessionSettings) { throw hresult_illegal_state_change(L"Cannot change CPU count after session has been initialized"); } if (value && value.Value() == 0) { throw hresult_invalid_argument(L"CPU count cannot be 0"); } m_cpuCount = value; }

注意这里使用的是value &&短路判断:只有当调用方确实传入了值时才校验是否为 0;传入nullptr表示不设置,走服务端默认。MemorySizeInMBMB 为单位,实现完全一致(SessionSettings.cpp),例如4096表示 4 GB。

3.3 Timeout:三层校验

Timeout是校验最复杂的属性,共三层:

  1. 不能为TimeSpan::zero()
  2. 换算成毫秒后不能为负;
  3. 换算成毫秒后必须能放进uint32_t(即不超过std::numeric_limits<uint32_t>::max())。

源码实现(SessionSettings.cpp):

void SessionSettings::Timeout(IReference<TimeSpan> const& value) { if (m_sessionSettings) { throw hresult_illegal_state_change(L"Cannot change timeout after session has been initialized"); } if (value) { if (value.Value() == TimeSpan::zero()) { throw hresult_invalid_argument(L"Timeout cannot be 0"); } // The C API takes the timeout in milliseconds as a uint32_t, // so we need to validate that the value is within range. auto timeoutMS = std::chrono::duration_cast<std::chrono::milliseconds>(value.Value()).count(); if (timeoutMS > std::numeric_limits<uint32_t>::max()) { throw hresult_invalid_argument(L"Timeout exceeds the allowed limit"); } if (timeoutMS < 0) { throw hresult_invalid_argument(L"Timeout cannot be negative"); } } m_timeout = value; }

这套校验与底层 C API 严格对齐:WslcSetSessionSettingsTimeout接收的是uint32_t timeoutMS(毫秒),因此 WinRT 层必须在把TimeSpan传给 C 层之前完成范围检查(见 wslcsdk.h)。

3.4 VhdRequirements:VHD 盘要求

VhdRequirements的类型是VhdOptions,setter 拒绝nullptr,并抛E_POINTER而非E_INVALIDARG

void SessionSettings::VhdRequirements(VhdOptions const& value) { if (m_sessionSettings) { throw hresult_illegal_state_change(L"Cannot change VHD requirements after session has been initialized"); } if (!value) { throw winrt::hresult_error(E_POINTER, L"VHD requirements cannot be null"); } m_vhdRequirements = value; }

VhdOptions的完整定义见 VhdOptions.cpp 与 wslcsdk.idl,构造签名为:

VhdOptions(String name, UInt64 size, VhdType type);
  • name:VHD 名称;
  • size:期望大小(字节),不能为 0,用于创建/扩容(create/expand);
  • typeVhdType::Dynamic(动态扩展)或VhdType::Fixed(固定大小);
  • OwnerIReference<VhdOwner>,uid/gid):仅命名卷(named volumes)支持;在SessionSettings.VhdRequirements上设置Owner会在属性设置阶段直接失败(E_INVALIDARG)。

这一限制在底层 C 结构 wslcsdk.h 中有明确注释:WslcVhdRequirements中的flagsuidgid字段“only honored by WslcCreateSessionVhdVolume”,而WslcSetSessionSettingsVhd会拒绝非NONE的 flags(返回E_INVALIDARG);name字段则被WslcSetSessionSettingsVhd忽略。

3.5 EnableGpu:通过功能标志实现

EnableGpu在实现上并不单独存储一个 bool,而是维护一个 32 位功能标志位掩码m_featureFlags,通过WI_IsFlagSet/WI_UpdateFlag读写(SessionSettings.cpp):

bool SessionSettings::EnableGpu() { return WI_IsFlagSet(m_featureFlags, WSLC_SESSION_FEATURE_FLAG_ENABLE_GPU); } void SessionSettings::EnableGpu(bool value) { if (m_sessionSettings) { throw hresult_illegal_state_change(L"Cannot change GPU setting after session has been initialized"); } WI_UpdateFlag(m_featureFlags, WSLC_SESSION_FEATURE_FLAG_ENABLE_GPU, value); }

对应的标志位定义在 wslcsdk.h:

typedef enum WslcSessionFeatureFlags { WSLC_SESSION_FEATURE_FLAG_NONE = 0x00000000, WSLC_SESSION_FEATURE_FLAG_ENABLE_GPU = 0x00000004 } WslcSessionFeatureFlags;

m_featureFlags的初始值是WSLC_SESSION_FEATURE_FLAG_NONE(即 0,等价于 GPU 关闭),成员定义见 SessionSettings.h。

四、不可变语义:物化(Materialization)机制

设置类的索引页 settings-classes/index.md 明确指出:

Settings objects become effectively immutable after the wrapper materializes the underlying C struct. (一旦包装器物化底层 C 结构,设置对象就变得事实上不可变。)

这个机制在SessionSettings::ToStructPointer()(SessionSettings.cpp)中实现:

WslcSessionSettings* SessionSettings::ToStructPointer() { if (m_sessionSettings) { return m_sessionSettings.get(); // 已物化,直接返回 } m_sessionSettings = std::make_unique<WslcSessionSettings>(); winrt::check_hresult(WslcInitSessionSettings(m_name.c_str(), m_storagePath.c_str(), m_sessionSettings.get())); if (m_cpuCount) { winrt::check_hresult(WslcSetSessionSettingsCpuCount(m_sessionSettings.get(), m_cpuCount.Value())); } if (m_memorySizeInMB) { winrt::check_hresult(WslcSetSessionSettingsMemory(m_sessionSettings.get(), m_memorySizeInMB.Value())); } if (m_timeout) { auto timeoutMS = std::chrono::duration_cast<std::chrono::milliseconds>(m_timeout.Value()).count(); winrt::check_hresult(WslcSetSessionSettingsTimeout(m_sessionSettings.get(), static_cast<uint32_t>(timeoutMS))); } if (m_vhdRequirements) { winrt::check_hresult(WslcSetSessionSettingsVhd(m_sessionSettings.get(), GetStructPointer(m_vhdRequirements))); } winrt::check_hresult(WslcSetSessionSettingsFeatureFlags(m_sessionSettings.get(), m_featureFlags)); return m_sessionSettings.get(); }

要点:

  • 惰性物化(lazy materialization):只有首次调用ToStructPointer()时才分配WslcSessionSettings并调用 C API 的WslcInitSessionSettings
  • 一次性写入:物化之后,所有 setter 都会抛hresult_illegal_state_change(例如 “Cannot change CPU count after session has been initialized”),保证底层 C 结构一旦生成就不再被改写;
  • 按需下发:只有显式设置过的可选属性(非nullptr)才会调用对应的WslcSetSessionSettings*系列函数;Timeout在此处完成TimeSpanuint32_t(毫秒)的最终转换;
  • 底层 C API 全集(见 wslcsdk.h):
STDAPI WslcInitSessionSettings(_In_ PCWSTR name, _In_ PCWSTR storagePath, _Out_ WslcSessionSettings* sessionSettings); STDAPI WslcCreateSession(_In_ WslcSessionSettings* sessionSettings, _Out_ WslcSession* session, _Outptr_opt_result_z_ PWSTR* errorMessage); STDAPI WslcSetSessionSettingsCpuCount(_In_ WslcSessionSettings* sessionSettings, _In_ uint32_t cpuCount); STDAPI WslcSetSessionSettingsMemory(_In_ WslcSessionSettings* sessionSettings, _In_ uint32_t memoryMB); STDAPI WslcSetSessionSettingsTimeout(_In_ WslcSessionSettings* sessionSettings, _In_ uint32_t timeoutMS); STDAPI WslcSetSessionSettingsVhd(_In_ WslcSessionSettings* sessionSettings, _In_opt_ const WslcVhdRequirements* vhdRequirements); STDAPI WslcSetSessionSettingsFeatureFlags(_In_ WslcSessionSettings* sessionSettings, _In_ WslcSessionFeatureFlags flags);

底层WslcSessionSettings是一个不透明结构(opaque struct),内部为BYTE _opaque[WSLC_SESSION_OPTIONS_SIZE],应用层无法直接窥探或修改其内容,只能通过上述 setter 函数写入(wslcsdk.h)。

五、官方示例:属性的设置与读取

原文档给出的完整示例覆盖了所有属性的“写”与“读”:

SessionSettings settings{ L"demo", L"C:\\WSLC\\demo" }; settings.Name(L"demo"); settings.StoragePath(L"C:\\WSLC\\demo"); settings.CpuCount(winrt::box_value<uint32_t>(4).as<winrt::Windows::Foundation::IReference<uint32_t>>()); settings.MemorySizeInMB(winrt::box_value<uint32_t>(4096).as<winrt::Windows::Foundation::IReference<uint32_t>>()); settings.Timeout(winrt::box_value(winrt::Windows::Foundation::TimeSpan{ std::chrono::minutes(5) }) .as<winrt::Windows::Foundation::IReference<winrt::Windows::Foundation::TimeSpan>>()); settings.EnableGpu(true); auto name = settings.Name(); auto path = settings.StoragePath(); auto cpu = settings.CpuCount(); auto memory = settings.MemorySizeInMB(); auto timeout = settings.Timeout(); auto enableGpu = settings.EnableGpu();

解读与实操提示:

  • 构造即校验L"demo"L"C:\\WSLC\\demo"都非空,构造合法;若 storagePath 目录不存在,底层会在创建会话时自动创建;
  • box_value+as<IReference<T>>:这是给 WinRTIReference<T>属性赋值时的惯用包装手法——先用winrt::box_value装箱,再as到对应的可空引用类型;CpuCount(4)MemorySizeInMB(4096)也可以直接传入整数字面量(C++/WinRT 会隐式转换),上面的写法是更显式的等价形式;
  • Timeoutstd::chrono表达std::chrono::minutes(5)表示 5 分钟,换算后为 300000 毫秒,落在uint32_t范围内,校验通过;
  • 读取返回值:getter 返回的值与设置值一一对应;未设置的属性将返回nullptr(如示例中未设置的VhdRequirements),可在读取后判空处理。

六、实战:从 SessionSettings 到完整会话生命周期

SessionSettings单独存在没有意义,它的消费方是Session类(session.md)。Session的构造函数直接接收SessionSettings(拒绝nullptr),之后通过Start()启动会话。

以下代码来自仓库的 end-to-end-example.md,完整演示了“检查前置条件 → 打印 SDK 版本 → 创建会话(4 CPU / 4 GB)→ 拉取 alpine 镜像 → 配置 init 进程 → 创建并启动容器 → 等待退出 → 清理”的全流程,其中第 1 步正是SessionSettings的典型用法:

#include <cstdio> #include <string> #include <chrono> #include <winrt/Microsoft.WSL.Containers.h> #include <winrt/Windows.Foundation.h> #include <winrt/Windows.Foundation.Collections.h> using namespace winrt; using namespace winrt::Microsoft::WSL::Containers; using namespace winrt::Windows::Foundation; using namespace winrt::Windows::Foundation::Collections; using namespace std::chrono_literals; int main() { init_apartment(); // 0. Check prerequisites auto missing = WslcService::GetMissingComponents(); if (missing != static_cast<Component>(0)) { printf("WSL components are missing. Run: wsl --install\n"); return 1; } auto ver = WslcService::GetVersion(); printf("WSL version: %u.%u.%u\n", ver.Major(), ver.Minor(), ver.Revision()); // 1. Create a session SessionSettings sessionSettings{ L"MyApp", L"C:\\WslcData" }; sessionSettings.CpuCount(4); sessionSettings.MemorySizeInMB(4096); Session session{ sessionSettings }; session.Start(); // 2. Pull an image PullImageOptions pullOpts{ L"docker.io/library/alpine:latest" }; auto pullOp = session.PullImageAsync(pullOpts); co_await pullOp; // 3. Configure an init process ProcessSettings initProcSettings; initProcSettings.OutputMode(ProcessOutputMode::Event); auto argv = single_threaded_vector<hstring>(); argv.Append(L"/bin/echo"); argv.Append(L"Hello from WSL Container!"); initProcSettings.CommandLine(argv); // 4. Configure and create a container ContainerSettings containerSettings{ L"alpine:latest" }; containerSettings.Name(L"hello-container"); containerSettings.InitProcess(initProcSettings); auto container = session.CreateContainer(containerSettings); // 5. Subscribe to init process events before starting auto initProcess = container.InitProcess(); auto exitedEvent = handle{ CreateEvent(nullptr, TRUE, FALSE, nullptr) }; int32_t initExitCode = -1; initProcess.OutputReceived([](auto const& data) { std::string text(data.begin(), data.end()); printf("%s", text.c_str()); }); initProcess.Exited(& { initExitCode = exitCode; SetEvent(exitedEvent.get()); }); // 6. Start the container container.Start(); // 7. Wait for the init process to exit (30-second timeout) WaitForSingleObject(exitedEvent.get(), 30000); printf("Process exited with code: %d\n", initExitCode); // 8. Clean up if (container.State() == ContainerState::Running) { container.Stop(Signal::SIGTERM, 10s); } container.Delete(DeleteContainerOption::None); session.Terminate(); return 0; }

关于Session的几个行为要点(session.md),与SessionSettings的使用强相关:

  • Start()是一次性的(one-shot):重复调用会抛异常;调用前会先完成SessionSettings的物化与 C 结构下发;
  • 多数方法内部会先EnsureStarted():因此SessionSettings的配置必须在Start()之前定稿——这也正是设置对象“物化后不可变”语义存在的根本原因;
  • 会话终止时会触发Terminated事件,进程崩溃会触发ProcessCrashed,示例中通过session.Terminate()显式收尾。

七、常见错误与排查速查

结合 Error 枚举 与 setter 校验,常见的失败场景如下:

场景失败表现解决办法
namestoragePath传空串构造/setter 抛hresult_invalid_argument传入非空字符串
机器上已存在同名会话创建会话返回ERROR_ALREADY_EXISTS更换会话名,或先终止旧会话
CpuCount/MemorySizeInMB传 0setter 抛hresult_invalid_argument传入 ≥1 的值,或不设置走默认
Timeout为 0 / 负 / 超uint32_t毫秒上限setter 抛hresult_invalid_argument使用正的、合理范围内的TimeSpan
VhdRequirementsnullptrsetter 抛E_POINTER先构造VhdOptions
在会话初始化后再改任何属性setter 抛hresult_illegal_state_changeSession.Start()/ 物化之前完成全部配置
会话名含敏感信息(凭据等)无运行时错误,但所有用户可见遵守安全规范,避免在名称中放敏感数据

结语

SessionSettings是 WSLC 容器会话的“总配置面板”:名称与存储路径奠定会话的身份和落盘位置,CPU/内存/超时/VHD/GPU 决定会话的运行能力,而“物化后不可变”的语义保证了配置在交给Session之后的一贯性。理解它的构造约束、属性校验与底层 C 桥接(WslcInitSessionSettings+ 系列 setter),是安全、正确地使用 WSL 容器 SDK 的第一步。进一步阅读可参考 Session 文档、settings-classes 索引 以及 完整的端到端示例。

【免费下载链接】WSLWindows Subsystem for Linux项目地址: https://gitcode.com/GitHub_Trending/ws/WSL

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询