基于vss的热迁移技术
来源:互联网 发布:mac os 11 编辑:程序博客网 时间:2024/05/16 01:58
声明:本文需要一定的虚拟化相关知识
vss(Volume Shadow Copy),是Windows下用于实现数据备份恢复的技术。本文要讲的是利用这种技术实现Windows系统的热迁移功能。
所谓热迁移,就是在操作系统处于正常运行的状态下,将系统迁移到指定IP的服务端,迁移内容除了磁盘上的数据,还包括内存当中的数据,内存数据的实时同步目前暂时不在本文中介绍。
vss能够实现在特定的时刻对操作系统上所有的磁盘分区制作卷影,也就是打一个快照。我们可以使用多数对原始分区有效的Windows系统api来访问快照,也就是说,快照同样可以被视为一个设备来进行数据读写操作,只是这个设备不会再有脏数据产生。
使用Windows自带的vssadmin命令可以查看快照(vssadmin list shadows)以及其他信息。
下面先贴出我这边在win7x86、2008x86以及2003x64系统上测试通过的快照代码:
/** * @brief 卷影复制demo * @author mrfang * @date 2015.11.18 */#include <Windows.h>#include <tchar.h>#include <shlwapi.h>#include <vss.h>#include <vswriter.h>#include <vsbackup.h>#include <VsProv.h>#define LOG_BUFFER_SIZE (4096 * 2)#define LogDebug mrlog#define LogInfo mrlog#define LogWarn mrlog#define LogError mrlog#define BUFFER_SIZE (4096)#pragma comment (lib, "ole32.lib")#pragma comment (lib, "VssApi.lib")#pragma comment (lib, "Advapi32.lib")// Helper macros to print a GUID using printf-style formatting#define WSTR_GUID_FMT _T("{%.8x-%.4x-%.4x-%.2x%.2x-%.2x%.2x%.2x%.2x%.2x%.2x}")#define GUID_PRINTF_ARG( X ) \ (X).Data1, \ (X).Data2, \ (X).Data3, \ (X).Data4[0], (X).Data4[1], (X).Data4[2], (X).Data4[3], \ (X).Data4[4], (X).Data4[5], (X).Data4[6], (X).Data4[7]void mrlog(const TCHAR* format, ...){ TCHAR szLogBuf[LOG_BUFFER_SIZE] = {0}; va_list arg_ptr; va_start(arg_ptr, format); _vsntprintf_s(szLogBuf, sizeof(szLogBuf)/sizeof(szLogBuf[0]), format, arg_ptr); va_end(arg_ptr); _tprintf(_T("%s\n"), szLogBuf);}void ReleaseInterface(IUnknown* unkn){ if (unkn) { unkn->Release(); unkn = NULL; }}/** * @brief 对指定逻辑卷创建卷影 * @param [in]szVolumeName:逻辑卷名 * @param [in]snapshotSetId:卷影副本集ID,此ID在AddToSnapshotSet后会修改为对应卷影副本的ID */BOOL CreateSnapshot(_In_ IVssBackupComponents* pBackup, _In_ const TCHAR* szVolumeName){ if (!pBackup) { LogError(_T("[CreateSnapshot]Invalid param")); return FALSE; } HRESULT hResult = S_OK; BOOL bRetVal = TRUE; VSS_ID snapShotId = {0}; IVssAsync* pPrepare = NULL; IVssAsync* pDoShadowCopy = NULL; VSS_SNAPSHOT_PROP snapshotProp = {0}; hResult = pBackup->AddToSnapshotSet(const_cast<TCHAR *>(szVolumeName), GUID_NULL, &snapShotId); if (hResult != S_OK) { LogError(_T("AddToSnapshotSet failed, code=0x%x"), hResult); bRetVal = FALSE; goto TheEnd; } hResult = pBackup->SetBackupState(false, false, /*VSS_BT_COPY*/VSS_BT_FULL); if (hResult != S_OK) { LogError(_T("SetBackupState failed, code=0x%x"), hResult); bRetVal = FALSE; goto TheEnd; } hResult = pBackup->PrepareForBackup(&pPrepare); if (hResult != S_OK) { LogError(_T("PrepareForBackup failed, code=0x%x"), hResult); bRetVal = FALSE; goto TheEnd; } LogInfo(_T("Preparing for backup...")); hResult = pPrepare->Wait(); if (hResult != S_OK) { LogError(_T("IVssAsync Wait failed, code=0x%x"), hResult); bRetVal = FALSE; goto TheEnd; } hResult = pBackup->DoSnapshotSet(&pDoShadowCopy); if (hResult != S_OK) { LogError(_T("DoSnapshotSet failed, code=0x%x"), hResult); bRetVal = FALSE; goto TheEnd; } LogInfo(_T("Taking snapshots...")); hResult = pDoShadowCopy->Wait(); if (hResult != S_OK) { LogError(_T("IVssAsync Wait failed, code=0x%x"), hResult); bRetVal = FALSE; goto TheEnd; } LogInfo(_T("Get the snapshot device object from the properties...")); hResult = pBackup->GetSnapshotProperties(snapShotId, &snapshotProp); if (hResult != S_OK) { LogError(_T("GetSnapshotProperties failed, code=0x%x"), hResult); bRetVal = FALSE; goto TheEnd; } LogDebug(_T(" Snapshot Id :") WSTR_GUID_FMT, GUID_PRINTF_ARG( snapshotProp.m_SnapshotId)); LogDebug(_T(" Snapshot Set Id ") WSTR_GUID_FMT, GUID_PRINTF_ARG(snapshotProp.m_SnapshotSetId)); LogDebug(_T(" Provider Id ") WSTR_GUID_FMT, GUID_PRINTF_ARG(snapshotProp.m_ProviderId)); LogDebug(_T(" OriginalVolumeName : %ls"), snapshotProp.m_pwszOriginalVolumeName); if (snapshotProp.m_pwszExposedName) LogDebug(_T(" ExposedName : %ls"), snapshotProp.m_pwszExposedName); if (snapshotProp.m_pwszExposedPath) LogDebug(_T(" ExposedPath : %ls"), snapshotProp.m_pwszExposedPath); if (snapshotProp.m_pwszSnapshotDeviceObject) LogDebug(_T(" DeviceObject : %ls"), snapshotProp.m_pwszSnapshotDeviceObject); VssFreeSnapshotProperties(&snapshotProp); bRetVal = TRUE;TheEnd: ReleaseInterface(pPrepare); ReleaseInterface(pDoShadowCopy); return bRetVal;}/** * @brief 创建卷影集 * @param [out]创建所得卷影集ID * @return TRUE:创建成功;FALSE:创建失败 */BOOL CreateSnapshotSet(_Out_ IVssBackupComponents** pBackup, _Out_ VSS_ID* snapshotSetId){ if (!pBackup || !snapshotSetId) { LogError(_T("[CreateSnapshotSet]Invalid param")); return FALSE; } IVssAsync *pAsync = NULL; HRESULT hResult = S_OK; BOOL bRetVal = TRUE; BOOL bFreeMetaData = FALSE; hResult = CoInitialize(NULL); if (hResult != S_OK) { LogError(_T("CoInitialize failed, code=0x%x"), hResult); return FALSE; } hResult = CreateVssBackupComponents(pBackup); //Release if no longer needed if (hResult != S_OK) { LogError(_T("CreateVssBackupComponents failed, code=0x%x"), hResult); return FALSE; } hResult = (*pBackup)->InitializeForBackup(); if (hResult != S_OK) { LogError(_T("InitializeForBackup failed, code=0x%x"), hResult); bRetVal = FALSE; goto TheEnd; } hResult = (*pBackup)->SetContext(VSS_CTX_BACKUP); if (hResult != S_OK) { LogError(_T("IVssBackupComponents SetContext failed, code=0x%x"), hResult); bRetVal = FALSE; goto TheEnd; } // Prompts each writer to send the metadata they have collected hResult = (*pBackup)->GatherWriterMetadata(&pAsync); bFreeMetaData = TRUE; if (hResult != S_OK) { LogError(_T("GatherWriterMetadata failed, code=0x%x"), hResult); bRetVal = FALSE; goto TheEnd; } hResult = pAsync->Wait(); if (hResult != S_OK) { LogError(_T("IVssAsync Wait failed, code=0x%x"), hResult); bRetVal = FALSE; goto TheEnd; } hResult = (*pBackup)->StartSnapshotSet(snapshotSetId); if (hResult != S_OK) { LogError(_T("StartSnapshotSet failed, code=0x%x"), hResult); bRetVal = FALSE; goto TheEnd; } bRetVal = TRUE;TheEnd: if (bFreeMetaData) (*pBackup)->FreeWriterMetadata(); return bRetVal;}/** * @brief 对指定的卷列表做卷影 * @param * @return */BOOL VolumeShadow(_In_ const TCHAR* szVolumeName){ if (!szVolumeName) { LogError(_T("[CopyVolume]Invalid param")); return FALSE; } VSS_ID snapshotSetID = {0}; IVssBackupComponents* pBackup = NULL; BOOL bRetVal = TRUE; LogInfo(_T("CreateSnapshotSet...")); if (!CreateSnapshotSet(&pBackup, &snapshotSetID)) { return FALSE; } if (!CreateSnapshot(pBackup, szVolumeName)) { LogError(_T("CreateSnapshot failed")); bRetVal = FALSE; } ReleaseInterface(pBackup); return bRetVal;}int _tmain(int argc, const TCHAR* argv[]){ if (argc != 2) { LogError(_T("Usage: %s volumeName. eg. %s C\\"), __FILE__, __FILE__); return 1; } VolumeShadow(argv[1]); return 0;}
对分区做快照之后,可以获取快照属性,其中有一项snapshotProp.m_pwszSnapshotDeviceObject便是分区所对应的快照名,于是便可以将此快照作为设备打开,从0字节处开始读取快照内部的数据。
为了加快拷贝效率,Windows提供了一个VOLUME_BITMAP_BUFFER结构,如下:
typedef struct {
LARGE_INTEGER StartingLcn;
LARGE_INTEGER BitmapSize;
BYTE Buffer[1];} VOLUME_BITMAP_BUFFER,
*PVOLUME_BITMAP_BUFFER;
在MSDN上有该结构的详细描述,该结构将分区中的数据用一个位图表示,因为磁盘上的分区数据是按簇为单位来进行读写的,因此该结构中用位图中1个bit代表1个簇来表示整个分区,bit值为1则表示对应簇写有数据,为0则表示未写入数据。因此位图的大小(按字节计)=分区大小(按字节计)/分区簇大小/8(表示1个字节8bit)。
该结构可以通过DeviceIoControl(FSCTL_GET_VOLUME_BITMAP)获取。
使用此位图,可以在读取分区数据的时候,仅读取bit值为1的簇,而跳过bit值为0的簇,并且幸运的是,这个操作针对快照也是有效的。
至此,基于vss的热迁移技术,其主要技术点已介绍完毕,数据发送以及通信部分略去。
下面分享一下在做这个的过程中遇到的问题以及解决方法:
(未完待续)
转载请注明原文链接,作者保留追究相关责任的权利。
- 基于vss的热迁移技术
- 基于libvert的虚拟机热迁移
- 基于Openstack的虚拟机热迁移
- 基于libvert的虚拟机热迁移
- VSS的迁移与修复
- Xen动态迁移的内存热拷贝技术
- VSS数据迁移
- 恒天云技术分享系列(一)—虚拟机热迁移
- Openstack不支持LVM后端的热迁移
- Android的热修复技术
- 基于游标和LOAD技术的DB2数据库高效数据迁移方法
- 热迁移流程
- ceph+configdrive+热迁移
- NameNode热迁移方案
- kilo版本规避config drive热迁移失败的问题
- nova 热迁移卷 的关键代码 rebase说明
- Android 热补丁技术——资源的热修复
- Android 热补丁技术——资源的热修复
- 知识点
- Android UI线程 HandlerThread 普通线程详解
- 百分点苏海波-用户画像的构建与使用1
- 解决ext+struts传递中文参数的乱码的问题
- 集合框架(List集合的一些特有方法)
- 基于vss的热迁移技术
- java 序列化 serialVersionUID transient
- POJ 2253
- 图标和图像大小
- java基础
- 【图论500】
- Found duplicate file for APK: assets/lineDashTexture.png
- ubuntu下执行sql脚本
- 【Leetcode】Intersection of Two Linked Lists