Windows 卸载的数据传输

ODX(卸载的数据传输)是一项加快服务器复制和移动操作速度的功能。 此功能从 Windows Server 2012 开始提供,在 NTFS 卷上受支持。 本页从文件系统和微型筛选器的角度介绍 ODX。 有关存储设备的信息,请参阅 Windows 存储卸载的数据传输

在计算机或同一台计算机之间传输数据是一种频繁的文件系统活动。 从功能的角度来看,使用标准 ReadFileWriteFile 函数非常有效,但它涉及通过系统的所有级别和可能跨网络进行大量数据移动。 这种复杂性可能会影响传输中涉及的系统的可用性和连接系统的网络连接。 许多存储子系统提供的高级功能为执行繁重的数据移动任务提供了更有效的方法。

应用程序可以利用这些功能来帮助将数据移动过程卸载到存储子系统。 文件系统筛选器通常可以通过拦截对卷的读写请求来监视这些操作。 筛选器需要执行更多操作才能了解 ODX。

典型数据传输

现在,在应用程序方案中移动数据更简单。 它涉及到将数据读取到本地内存中,然后将其写回到新位置。 下图演示了此方案。

显示典型数据传输的关系图。

此方案涉及在两个不同的文件服务器上两个位置之间复制文件,每个文件服务器都有自己的虚拟磁盘,通过智能存储阵列 (ISA) 公开。 启动系统首先需要将数据从源虚拟磁盘读取到本地缓冲区中。 然后,它通过某些传输和协议(如 1 GbE 上的 SMB)将数据打包并传输到第二个系统。 然后,第二个系统接收数据并将其输出到本地缓冲区。 然后,目标系统将数据写入目标虚拟磁盘。 此方案描述了一种典型的数据传输读/写方法,每天由许多不同的应用程序多次执行。

尽管大多数情况下标准读取和写入效果良好,但打算复制的数据可能位于由同一智能存储阵列管理的虚拟磁盘上。 这种情况意味着数据从阵列中移出,移动到服务器上,通过网络传输,移动到另一台服务器上,然后再次移回同一阵列。 在服务器内和网络传输中移动数据的行为可能会显著影响这些系统的可用性。 此外,数据移动的吞吐量受网络吞吐量和可用性的限制。

卸载的数据传输 (ODX)

卸载数据传输

Windows 8 中引入了两个 FSCTL,有助于卸载数据传输的方法。 这种卸载将位移动的负担从服务器转移到存储子系统内智能发生的位移动。 可视化命令语义的最佳方法是将其视为类似于无缓冲读取和无缓冲写入。

  • FSCTL_OFFLOAD_READ

    此控制请求在要读取的文件中使用偏移量,并在 FSCTL_OFFLOAD_READ_INPUT 结构中使用所需的长度。 如果受支持,则托管文件的存储子系统会收到关联的卸载读取存储命令。 然后,它会生成一个令牌,该令牌是在卸载读取命令时要读取的数据的逻辑表示形式。 此令牌字符串以 FSCTL_OFFLOAD_READ_OUTPUT 结构返回给调用方。

  • FSCTL_OFFLOAD_WRITE

    此控制请求获取要写入的文件内的偏移量、所需的写入长度以及作为要写入的数据的逻辑表示的令牌。 如果受支持,则托管要写入的文件的存储子系统会收到关联的卸载写入存储命令。 它首先尝试识别给定的令牌,然后在可能的情况下执行写入操作。 写入操作在 Windows 下完成,因此文件系统和存储堆栈上的组件看不到数据移动。 数据移动完成后,写入的字节数将返回到调用方。

与第一个图类似,下图显示了两个不同服务器上的两个虚拟磁盘之间的简单文件副本。

显示卸载的数据传输的关系图。

然而,我们没有进行正常的读写操作,而是将繁重的位移动任务转移到存储阵列上。 第一个系统发出卸载读取操作,请求阵列生成一个令牌,该令牌表示在第一个虚拟磁盘的区域内要读取的数据的时间点视图。 然后,第一个系统将令牌传输到第二个系统,第二个系统又使用令牌向第二个虚拟磁盘发出卸载写入操作。 然后,阵列解释令牌并尝试在虚拟磁盘之间执行数据移动。 实际的数据传输发生在智能存储阵列内,而不是在两台主机之间。 这种设计显著提高了两个系统的可用性,同时几乎消除了系统之间的网络流量。

与复制引擎集成

Windows 中的核心复制引擎由 CopyFile 和相关函数使用。 从 Windows 8 开始,复制引擎透明地尝试在传统的复制文件代码路径之前使用 ODX。 复制 API 由大多数应用程序、实用工具和 shell 使用。 默认情况下,这些调用者能够使用 ODX 功能,几乎不需要修改代码或用户干预。

以下步骤总结了复制引擎如何尝试 ODX:

  1. 复制引擎对源文件发出 FSCTL_OFFLOAD_READ,以获取读取令牌。
  2. 如果在检索读取令牌时失败,复制引擎会回退到传统的读取和写入(传统的复制文件代码路径)。 如果故障表明源卷不支持卸载,则复制引擎还会在每个进程的缓存中标记该卷。 复制引擎不再尝试卸载每个进程缓存中的卷。
  3. 如果成功检索到令牌,则复制引擎会尝试在大型区块中对目标文件发出 FSCTL_OFFLOAD_WRITE 命令,直到令牌逻辑上表示的所有数据都被卸载写入。
  4. 执行卸载读/写的任何错误都会导致复制引擎回退到传统的读/写代码路径。 此回退从卸载代码路径结束的位置(读取或写入被截断的位置)开始。 复制引擎更新相同的每进程缓存,因此如果满足以下任何一个条件,它不会尝试卸载这些卷。 此每进程缓存会定期重置。
  • 该故障表示目标卷不支持卸载。
  • 源卷无法访问目标卷。

以下函数支持 ODX:

  • CopyFile
  • CopyFileEx
  • MoveFile
  • MoveFileEx
  • CopyFile2

以下函数不支持 ODX:

  • CopyFileTransacted
  • MoveFileTransacted

支持的卸载的数据传输方案

Hyper-V 存储堆栈和 Windows SMB 文件服务器中提供了对卸载操作的支持。 如果后备物理存储支持 ODX 操作,则调用方可以向驻留在 VHD 或远程文件共享上的文件发出 FSCTL_OFFLOAD_READFSCTL_OFFLOAD_WRITE,无论是从虚拟机内部还是物理硬件。 下图说明了 ODX 支持的最基本支持的源和目标。

显示卸载的数据传输方案的关系图。

文件系统筛选器选择加入模型及其对应用程序的影响

从 Windows 8 开始,筛选器管理器允许筛选器将卸载功能指定为受支持的功能。 附加到卷的文件系统筛选器可以共同确定是否支持特定的卸载操作。 如果不支持,操作将失败,并显示相应的错误代码。

筛选器必须通过名为 SupportedFeatures 的注册表 DWORD 值来指示它支持 FSCTL_OFFLOAD_READFSCTL_OFFLOAD_WRITE,该值位于 HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\filter driver name\ 的注册表驱动程序服务定义中。 此值包含位字段,其中的位决定了选择的功能,应在筛选器安装期间设置。

目前,定义的位为:

标记 含义
SUPPORTED_FS_FEATURES_OFFLOAD_READ 0x00000001 筛选器支持 FSCTL_OFFLOAD_READ
SUPPORTED_FS_FEATURES_OFFLOAD_WRITE 0x00000002 筛选器支持 FSCTL_OFFLOAD_WRITE

可以根据 HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\FileSystem\FilterSupportedFeaturesMode 注册表项中的值启用或禁用筛选器选择加入模型,该注册表项具有以下值:

FilterSupportedFeaturesMode 值 含义
0(默认值) 进行正常的选择加入处理。
1 从不选择加入(相当于在附加的所有筛选器上将 SupportedFeatures 设置为 0)

测试

若要检查筛选器支持的堆栈功能,请使用 fltmc 实用工具。 以提升的用户身份运行 fltmc instances –v [volume]:,并检查 SprtFtrs 列:

  • 如果 SprtFtrs 值为 0x00,则表示筛选器正在阻止此卷上的卸载。 如果 SprtFtrs 设置为 0x03,则支持这两个卸载操作。

检查 IRP 处理中的功能支持

作为 IRP 处理的一部分,FsRtlGetSupportedFeatures 例程检索附加到给定卷堆栈的所有筛选器的聚合 SupportedFeatures 状态。 I/O 管理器和 SRV (SMB) 等组件调用此例程来验证堆栈上所有筛选器的 SupportedFeatures 状态。 滚动更新其自己的卸载 IRP 的组件应调用此函数来验证对此操作的选择加入支持。

筛选器驱动程序的注意事项

ODX 是在数据中心中移动数据的一种方式。 由于在核心复制引擎中集成了卸载逻辑,因此在默认情况下,许多应用程序无需显式选择即可执行卸载数据移动。 因此,筛选器开发人员需要了解这些操作如何影响筛选器。 不完全理解这些操作或不评估数据流的变化可能会导致数据不一致或损坏的情况。 以下列表总结了筛选器开发人员在卸载时需要注意的一组操作项:

  • 了解此数据流、对筛选器的影响以及筛选器支持这些卸载操作的能力。
  • 更新筛选器安装程序,将 SupportedFeatures 的 REG_DWORD 值添加到 HKLM\System\CurrentControlSet\Services\[filter] 子项。 初始化,以指定卸载功能。
  • 对于希望对卸载操作采取行动的筛选器,请将注册更新为 IRP_MJ_FILE_SYSTEM_CONTROL,以处理 FSCTL_OFFLOAD_READFSCTL_OFFLOAD_WRITE
  • 对于需要阻止卸载操作的筛选器,请从筛选器中返回状态代码 STATUS_NOT_SUPPORTED。 不要依赖注册表值来强制实施阻止卸载操作,因为最终用户可以更改它。 筛选器应显式允许或禁止卸载操作。

复制令牌

使用卸载的操作,I/O 堆栈看不到文件数据。 相反,文件数据被视为 512 字节的令牌,是数据的逻辑代理。 此令牌是:

  • 由存储子系统生成的供应商特定格式的不透明且唯一的字符串。
  • 一种已知的表示数据模式的类型(例如,逻辑上等于零的数据范围)。

对代理令牌数据的修改会导致令牌无效,或者存储子系统通过某些特定于供应商的方式(如通过快照机制)持久化原始数据。 后续对文件中指定范围的卸载读取请求会产生唯一的令牌。

有几类令牌表示定义良好的数据模式。 最常见的已知令牌是零令牌,它等效于零。 当一个令牌被定义为已知令牌时,STORAGE_OFFLOAD_TOKEN 结构中的 TokenType 成员被设置为 STORAGE_OFFLOAD_TOKEN_TYPE_WELL_KNOWN。 设置此字段时,WellKnownPattern 成员确定令牌是哪种数据模式。

  • 如果 WellKnownPattern 字段设置为 STORAGE_OFFLOAD_PATTERN_ZERO 或 STORAGE_OFFLOAD_PATTERN_ZERO_WITH_PROTECTION_INFORMATION,则表示零令牌。 当此令牌由 FSCTL_OFFLOAD_READ 操作返回时,它表示所需文件范围内包含的数据在逻辑上等于零。 当将此令牌提供给 FSCTL_OFFLOAD_WRITE 操作时,它表示要写入的文件的所需范围应在逻辑上为零。
  • 除了零令牌之外,目前还没有定义其他已知的令牌模式。 不建议用户定义自己的已知令牌模式。

截断

与 Windows 通信的基础存储子系统可以处理卸载操作中所需的较少数据。 这种情况称为截断。 通过卸载读取,返回的令牌表示的数据范围小于请求的数据范围。 FSCTL_OFFLOAD_READ_OUTPUT 结构中的 TransferLength 成员用于指示此值,该值是从要读取的文件范围开始的字节计数。 对于卸载写入,截断表示写入的数据比预期的要少。 FSCTL_OFFLOAD_WRITE_OUTPUT 结构中的 LengthWritten 成员指示此值,该值是从要写入的文件范围开始的字节计数。 命令处理中的错误或堆栈中对大范围的限制会导致截断。

在两种情况下,NTFS 会截断要卸载读取或写入的范围:

  1. 如果有效数据长度 (VDL) 在文件结尾 (EOF) 之前,则复制范围将截断为 VDL。 此操作假定 VDL 与逻辑扇区边界保持一致,否则请参阅方案。

    显示 VDL 发生在 EOF 之前的关系图。

    FSCTL_OFFLOAD_READ 操作期间,在 FSCTL_OFFLOAD_READ_OUTPUT 结构中设置标志 OFFLOAD_READ_FLAG_ALL_ZERO_BEYOND_CURRENT_RANGE,表示文件的其余部分包含零,并且 TransferLength 成员被截断为 VDL。

  2. 与方案 1 类似,但当 VDL 与逻辑扇区边界不一致时,NTFS 会将所需范围截断到下一个逻辑扇区边界。

    显示 VDL 与扇区边界不对齐的关系图。

限制

  • 仅在 NTFS 卷上支持卸载操作。
  • 如果以下条件均为 true,则通过远程文件服务器支持卸载操作:
    • 远程共享是 NTFS 卷。
    • 服务器正在运行 Windows Server 2012 或更高版本(假设远程堆栈也支持卸载操作)。
  • NTFS 不支持对使用 Bitlocker 或 NTFS 加密 (EFS) 加密的文件、已删除重复数据的文件、压缩文件、驻留文件、稀疏文件或参与 TxF 事务的文件执行卸载 FSCTL。
  • NTFS 不支持对 volsnap 快照中的文件执行卸载 FSCTL。
  • 如果以下条件之一为 true,NTFS 将无法卸载 FSCTL。 此方法遵循与非缓存 IO 相同的语义。
    • 所需的文件范围与源设备上的逻辑扇区大小不对齐。
    • 所需的文件范围与目标设备上的逻辑扇区大小不对齐。
  • FSCTL_OFFLOAD_WRITE 之前,必须预先分配目标文件((SetEndOfFile,而不是 SetAllocation)。
  • 当 NTFS 处理卸载读取和写入时,它首先调用 CcCoherencyFlushAndPurgeCache,将任何修改过的数据提交到系统缓存中。 此操作与非缓存 IO 具有相同的语义。