news 2026/9/10 11:00:30

WSL C SDK 存储 API 深度解析:WslcCreateSessionVhdVolume 与 WslcDeleteSessionVhdVolume 的用法、校验逻辑与源码实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
WSL C SDK 存储 API 深度解析:WslcCreateSessionVhdVolume 与 WslcDeleteSessionVhdVolume 的用法、校验逻辑与源码实现

WSL C SDK 存储 API 深度解析:WslcCreateSessionVhdVolume 与 WslcDeleteSessionVhdVolume 的用法、校验逻辑与源码实现

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

本篇围绕 WSL(Windows Subsystem for Linux)仓库中的 C API Storage APIs 参考页 展开,完整覆盖其中登记的两个成员函数WslcCreateSessionVhdVolumeWslcDeleteSessionVhdVolume的签名、参数、取值约束与调用示例,并深入 wslcsdk.cpp 的校验与底层CreateVolume调用链、wslcsdk.h 中的WslcVhdRequirements结构定义以及 WslcSdkTests.cpp 中的端到端测试用例,帮助读者掌握在 WSLC(Windows Subsystem for Linux Containers)会话中创建、挂载并删除命名 VHD 卷的完整实战方案。

1. 存储 API 的定位:会话级命名 VHD 卷管理

Storage APIs是 C API 参考目录 下的一个子章节,当前登记了两个成员函数:

  • WslcCreateSessionVhdVolume:在已运行的 WSLC 会话中创建一个以 VHDX 为后端的命名卷;
  • WslcDeleteSessionVhdVolume:按名称删除该会话中的命名卷。

这两个函数管理的对象是会话内的命名卷(named session volumes),区别于会话自身的根文件系统 VHD(后者由WslcSetSessionSettingsVhd在建会话前配置)。命名卷创建后,可通过容器配置中的WslcSetContainerSettingsNamedVolumes挂载进容器,实现跨容器的持久化数据共享。

2. WslcCreateSessionVhdVolume:签名、参数与官方示例

2.1 函数签名与参数表

STDAPI WslcCreateSessionVhdVolume( _In_ WslcSession session, _In_ const WslcVhdRequirements* options, _Outptr_opt_result_z_ PWSTR* errorMessage);
ParameterTypeDirection
sessionWslcSessionin
optionsconst WslcVhdRequirements*in
errorMessagePWSTR*out, optional

返回值:HRESULT,成功时返回S_OK,失败时返回对应的错误码(如E_INVALIDARGE_POINTER)。

2.2 WslcVhdRequirements 结构:字段语义与生效条件

入参结构体WslcVhdRequirements的完整定义见 wslcsdk.h:

typedef enum WslcVhdType { WSLC_VHD_TYPE_DYNAMIC = 0, // Expanding VHDX (default) WSLC_VHD_TYPE_FIXED = 1 // Fixed-allocation VHDX (only honored by WslcCreateSessionVhdVolume) } WslcVhdType; typedef enum WslcVhdRequirementsFlags { WSLC_VHD_REQ_FLAG_NONE = 0x00000000, // When set, WslcVhdRequirements::uid and gid are honored. When clear, // those fields are ignored and the volume is left owned by root:root. WSLC_VHD_REQ_FLAG_OWNER = 0x00000001, } WslcVhdRequirementsFlags; typedef struct WslcVhdRequirements { // Ignored by WslcSetSessionSettingsVhd _In_z_ PCSTR name; _In_ uint64_t sizeBytes; // Desired size (for create/expand) _In_ WslcVhdType type; // The remaining fields are only honored by WslcCreateSessionVhdVolume. // WslcSetSessionSettingsVhd rejects non-NONE flags with E_INVALIDARG. _In_ WslcVhdRequirementsFlags flags; _In_ uint32_t uid; // honored iff (flags & WSLC_VHD_REQ_FLAG_OWNER) _In_ uint32_t gid; // honored iff (flags & WSLC_VHD_REQ_FLAG_OWNER) } WslcVhdRequirements;

各字段在WslcCreateSessionVhdVolume中的约束如下:

字段类型约束与语义
namePCSTR卷名,不能为NULL,否则返回E_INVALIDARG;对应后端文件<会话存储目录>/volumes/<name>.vhdx
sizeBytesuint64_t期望容量(字节),必须大于 0,否则返回E_INVALIDARG
typeWslcVhdType仅接受WSLC_VHD_TYPE_DYNAMIC(0,动态扩展 VHDX,默认)或WSLC_VHD_TYPE_FIXED(1,固定分配 VHDX);其它取值返回E_INVALIDARGWSLC_VHD_TYPE_FIXED仅被本函数认可的取值
flagsWslcVhdRequirementsFlags目前已知标志位仅有WSLC_VHD_REQ_FLAG_OWNER;出现任何未知标志位会返回E_INVALIDARG(防未来标志位被静默忽略)
uid/giduint32_t仅当flags & WSLC_VHD_REQ_FLAG_OWNER时生效,指定卷根 inode 的所有者;未设置该标志时字段被忽略,卷保持root:root属主

2.3 官方示例(可直接复现)

参考文档给出的最小示例:

WslcVhdRequirements options = { 0 }; options.name = "cache"; options.sizeBytes = (uint64_t)8 * 1024 * 1024 * 1024; // 8 GiB options.type = WSLC_VHD_TYPE_DYNAMIC; options.flags = WSLC_VHD_REQ_FLAG_OWNER; options.uid = (uint32_t)1000; options.gid = (uint32_t)1000; HRESULT hr = WslcCreateSessionVhdVolume(session, &options, NULL);

errorMessage为可选出参:传入非空指针时,失败路径会返回一条以CoTaskMemAlloc分配的、调用方需自行释放(CoTaskMemFree)的宽字符错误消息;不需要消息时可传NULL(测试代码中大量使用NULL)。

3. 源码级实现剖析:从参数校验到底层 CreateVolume

3.1 参数校验顺序与错误码

实现位于 wslcsdk.cpp#L482-L533,校验顺序非常明确,可直接映射为一张错误码表:

auto internalType = CheckAndGetInternalType(session); RETURN_HR_IF_NULL(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), internalType->session); RETURN_HR_IF_NULL(E_POINTER, options); RETURN_HR_IF_NULL(E_INVALIDARG, options->name); RETURN_HR_IF(E_INVALIDARG, options->sizeBytes == 0); // Reject unknown flag bits so future additions can't be silently ignored. constexpr WslcVhdRequirementsFlags c_knownFlags = WSLC_VHD_REQ_FLAG_OWNER; RETURN_HR_IF(E_INVALIDARG, (options->flags & ~c_knownFlags) != WSLC_VHD_REQ_FLAG_NONE);
触发条件返回码
会话句柄内部状态无效(非活动会话)HRESULT_FROM_WIN32(ERROR_INVALID_STATE)
options == NULLE_POINTER
options->name == NULLE_INVALIDARG
options->sizeBytes == 0E_INVALIDARG
flags含未知标志位(如0x80000000E_INVALIDARG
type既不是DYNAMIC也不是FIXED(如强转值 42)E_INVALIDARG

上述每一个错误分支都在 WslcSdkTests.cpp#L2184-L2212 与 L2269-L2277 中有对应的负向用例逐一验证。

3.2 驱动选项组装:SizeBytes / Fixed / Uid / Gid

校验通过后,SDK 把WslcVhdRequirements翻译为一组WSLCCompatDriverOption(键值对)传给底层卷创建接口:

const auto sizeStr = std::to_string(options->sizeBytes); std::vector<WSLCCompatDriverOption> driverOpts; driverOpts.push_back({"SizeBytes", sizeStr.c_str()}); if (options->type == WSLC_VHD_TYPE_FIXED) { driverOpts.push_back({"Fixed", "true"}); } if (WI_IsFlagSet(options->flags, WSLC_VHD_REQ_FLAG_OWNER)) { driverOpts.push_back({"Uid", uidStr.c_str()}); driverOpts.push_back({"Gid", gidStr.c_str()}); } WSLCCompatVolumeOptions volumeOptions{}; volumeOptions.Name = options->name; volumeOptions.Driver = "vhd"; volumeOptions.DriverOpts = driverOpts.data(); ... return errorInfoWrapper.CaptureResult(internalType->session->CreateVolume(&volumeOptions, &volumeInfo));

由此可以得到几个实现层面的事实:

  • 卷的后端驱动固定为vhd,即每个命名卷对应一个 VHDX 文件;从测试代码断言的路径看,文件落在<会话存储目录>/volumes/<name>.vhdx(见 WslcSdkTests.cpp#L2128-L2129);
  • WSLC_VHD_TYPE_FIXED通过追加Fixed=true驱动选项实现固定预分配;单元测试进一步验证固定卷落盘后的文件尺寸不小于sizeBytes(L2214-L2231),而动态卷则按需扩展;
  • OWNER标志转换为Uid/Gid两个驱动选项。测试中的注释表明其作用时机:uid/gid 在mkfs时写入卷的根 inode,随后容器内执行stat -c "%u %g" /data得到65534 65534nobody:nogroup)作为验证(L2233-L2267);
  • 源码中特意把uidStr/gidStr提升到函数作用域,注释解释了原因:驱动选项数组中保存的是c_str()指针,必须保持到CreateVolume调用结束都有效(L498-L502)。

3.3 与 WslcSetSessionSettingsVhd 的边界差异

WslcVhdRequirements同时被WslcSetSessionSettingsVhd(配置会话根文件系统 VHD)复用,但两者对字段的认可范围不同,这一点从 wslcsdk.cpp#L548-L572 可见:

  • WslcSetSessionSettingsVhd忽略name(根文件系统卷不需要名字);
  • 只接受WSLC_VHD_TYPE_DYNAMICFIXED会返回E_NOTIMPL—— 这也正是头文件中“WSLC_VHD_TYPE_FIXEDis only honored byWslcCreateSessionVhdVolume”备注的由来;
  • 要求flags必须为WSLC_VHD_REQ_FLAG_NONE,否则E_INVALIDARG,防止调用者误以为 owner 设置作用在了根文件系统 VHD 上;
  • NULL时重置为默认值(sizeBytes = s_DefaultStorageSize)。

换言之:WslcCreateSessionVhdVolume才是该结构体全部字段(含nametype=FIXEDOWNER标志)的“全量消费者”,这是文档头注两条 Header notes 的完整背景。

4. WslcDeleteSessionVhdVolume:签名与实现

4.1 签名与参数

STDAPI WslcDeleteSessionVhdVolume( _In_ WslcSession session, _In_z_ PCSTR name, _Outptr_opt_result_z_ PWSTR* errorMessage);
ParameterTypeDirection
sessionWslcSessionin
namePCSTRin(_In_z_,指向以空字符终止的卷名)
errorMessagePWSTR*out, optional

返回值:HRESULT

参考文档示例:

HRESULT hr = WslcDeleteSessionVhdVolume(session, "cache", NULL);

4.2 实现与语义

实现非常短,位于 wslcsdk.cpp#L535-L546:校验会话处于有效状态(否则ERROR_INVALID_STATE)、name非空(否则E_POINTER),然后直接转发到底层session->DeleteVolume(name)

从单元测试(WslcSdkTests.cpp#L2174-L2182)可确认其完整语义:删除成功后,对应的<会话存储目录>/volumes/<name>.vhdx文件必须已从磁盘移除。因此删除是不可逆的——卷内数据随之丢失,生产环境中应在删除前把关键数据拷出或先停止挂载该卷的容器。

5. 端到端实战:创建卷并用容器读写

测试用例(WslcSdkTests.cpp#L2100-L2182)提供了一条完整可参考的调用链,展示了命名卷的典型生命周期:

  1. 建会话WslcInitSessionSettings+WslcCreateSession,测试刻意使用独立会话目录,避免影响共享默认会话;
  2. 建卷WslcCreateSessionVhdVolume创建名为c_volumeName的动态卷,并断言后端 VHDX 文件存在;
  3. 挂载写入:通过WslcSetContainerSettingsNamedVolumes把命名卷挂进容器的/data
WslcContainerNamedVolume namedVol{}; namedVol.name = c_volumeName; // 与 WslcVhdRequirements.name 一致 namedVol.containerPath = "/data"; // 容器内绝对路径 namedVol.readOnly = FALSE; WslcSetContainerSettingsNamedVolumes(&containerSettings, &namedVol, 1);

容器执行echo wslc-vhd-test > /data/marker.txt写入标记文件(WslcContainerNamedVolume定义见 wslcsdk.h#L205-L210); 4.跨容器读取:第二个容器以readOnly = TRUE只读挂载同一卷,cat /data/marker.txt读回wslc-vhd-test\n,验证了命名卷在会话内多个容器之间的数据共享; 5.删卷WslcDeleteSessionVhdVolume后断言 VHDX 文件不再存在。

6. WinRT 封装对照:VhdOptions 与 Session 接口

同一组 API 在 WinRT 侧有面向托管/WinRT 调用者的封装。VhdOptions的构造函数接收namesizeVhdType,其中size == 0会在构造/赋值阶段即抛出E_INVALIDARG("VHD size cannot be zero",见 VhdOptions.cpp#L21-L28);当设置了Owner(含Uid/Gid的可选引用)时,ToStructPointer()会把它们填入WslcVhdRequirements并自动置位WSLC_VHD_REQ_FLAG_OWNER(VhdOptions.cpp#L95-L114)。会话接口CreateVhdVolume/DeleteVhdVolume则直接桥接到本文讲解的两个 C 函数(Session.cpp#L322-L336)。WinRT 单元测试(WslcSdkWinRTTests.cpp#L1523-L1591)覆盖了动态卷、固定卷与 owner 卷三条路径,行为与 C API 一致。

7. 要点小结

  • WslcCreateSessionVhdVolume接受WslcVhdRequirements全部字段name(卷名)、sizeBytes(必须 > 0)、typeDYNAMIC或仅本函数认可的FIXED)、OWNER标志(控制uid/gid是否生效,作用于卷根 inode 属主);任何未知标志位或非法type都以E_INVALIDARG拒绝。
  • 卷以<会话存储目录>/volumes/<name>.vhdx落盘,驱动选项为SizeBytes/Fixed=true/Uid/Gid,创建动作最终转发到底层CreateVolume(wslcsdk.cpp#L524-L531)。
  • 创建出的命名卷通过WslcSetContainerSettingsNamedVolumes挂载进容器,可读写或只读,实现会话内多容器共享持久化数据。
  • WslcDeleteSessionVhdVolume按名删除卷并移除后端 VHDX 文件,操作不可逆;其参数校验(E_POINTER/ERROR_INVALID_STATE)与创建函数一致。
  • WslcSetSessionSettingsVhd(根文件系统 VHD 配置)相比,会话命名卷 API 是唯一支持固定分配 VHD 与属主设置的入口,这是本组存储 API 最核心的差异化能力。

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

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 10:59:38

嵌入式QT车载影音系统C++源码:从工程结构到真机部署

简介&#xff1a;这是一套面向嵌入式与Qt开发学习者的车载影音系统完整工程&#xff0c;基于C在Qt/Embedded环境下实现&#xff0c;涵盖天气、视频、音乐、地图四大功能模块。天气模块通过HTTP请求并解析JSON数据展示未来5天预报&#xff1b;视频与音乐模块调用mplayer进程并支…

作者头像 李华
网站建设 2026/9/10 10:56:07

WeChatMsg 微信聊天记录备份完整指南:3 个任务导出成 Word 和 PDF

WeChatMsg 微信聊天记录备份完整指南&#xff1a;3 个任务导出成 Word 和 PDF 【免费下载链接】WeChatMsg 提取微信聊天记录&#xff0c;将其导出成HTML、Word、CSV文档永久保存&#xff0c;对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trendi…

作者头像 李华
网站建设 2026/9/10 10:53:40

STM32F103四路独立PWM硬件配置与同步控制实战

简介&#xff1a;本资源是一套基于STM32F10x系列的四路PWM输出完整工程实现&#xff0c;面向嵌入式初学者与电机控制开发者&#xff0c;解决多路独立PWM信号生成、频率与占空比精准调控等核心问题&#xff0c;适用于直流电机双路驱动、LED调光、电源控制等典型应用场景。压缩包…

作者头像 李华
网站建设 2026/9/10 10:53:29

化工反应釜PLC控制系统设计与组态王应用实践

1. 项目背景与核心需求解析 化工生产中的加热反应釜控制一直是工业自动化领域的经典课题。去年我在某中型化工厂参与改造的老式反应釜控制系统&#xff0c;正是采用三菱FX2N系列PLC作为主控制器。这个40kW的夹套式反应釜&#xff0c;需要精确控制反应物温度在1℃范围内&#xf…

作者头像 李华