基于vss的热迁移技术

来源:互联网 发布:mac os 11 编辑:程序博客网 时间:2024/05/16 01:58

声明:本文需要一定的虚拟化相关知识

vssVolume Shadow Copy),是Windows下用于实现数据备份恢复的技术。本文要讲的是利用这种技术实现Windows系统的热迁移功能。

所谓热迁移,就是在操作系统处于正常运行的状态下,将系统迁移到指定IP的服务端,迁移内容除了磁盘上的数据,还包括内存当中的数据,内存数据的实时同步目前暂时不在本文中介绍。

vss能够实现在特定的时刻对操作系统上所有的磁盘分区制作卷影,也就是打一个快照。我们可以使用多数对原始分区有效的Windows系统api来访问快照,也就是说,快照同样可以被视为一个设备来进行数据读写操作,只是这个设备不会再有脏数据产生。

    使用Windows自带的vssadmin命令可以查看快照(vssadmin list shadows)以及其他信息。

下面先贴出我这边在win7x862008x86以及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上有该结构的详细描述,该结构将分区中的数据用一个位图表示,因为磁盘上的分区数据是按簇为单位来进行读写的,因此该结构中用位图中1bit代表1个簇来表示整个分区,bit值为1则表示对应簇写有数据,为0则表示未写入数据。因此位图的大小(按字节计)=分区大小(按字节计)/分区簇大小/8(表示1个字节8bit)。

    该结构可以通过DeviceIoControl(FSCTL_GET_VOLUME_BITMAP)获取。

    使用此位图,可以在读取分区数据的时候,仅读取bit值为1的簇,而跳过bit值为0的簇,并且幸运的是,这个操作针对快照也是有效的。

    至此,基于vss的热迁移技术,其主要技术点已介绍完毕,数据发送以及通信部分略去。

 

 

 

下面分享一下在做这个的过程中遇到的问题以及解决方法:

(未完待续)



转载请注明原文链接,作者保留追究相关责任的权利。

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 鼻炎鼻涕跟水怎么办 出门忘记带钥匙怎么办 总留鼻涕水怎么办 流像水一样的鼻涕怎么办 出门忘记带洗面奶怎么办 一侧鼻子流清水怎么办 慢性肠胃炎犯了怎么办 儿子拉肚肚子疼怎么办 皮肤太容易过敏怎么办 过敏脸一直不好怎么办 脸上突然过敏怎么办急救 脸过敏发红疼怎么办 脸上突然过敏了怎么办 脸过敏总反复怎么办 用蜂蜜洗脸过敏怎么办 脸上涂蜂蜜过敏怎么办 孕晚期脸过敏怎么办 吃芒果脸过敏怎么办 脸上老反复过敏怎么办 用什么都过敏怎么办 脸上起过敏湿疹怎么办 脸上发湿疹过敏怎么办 感冒流鼻涕有痰怎么办 咳嗽流清水鼻涕怎么办 脸最近老是过敏怎么办 鼻炎脸特别疼怎么办 宝宝感冒一直流鼻涕怎么办 经常过敏的体质怎么办 一边鼻子堵了怎么办 鼻炎流清水鼻涕怎么办 鼻子总感觉堵塞怎么办 半边鼻子不通气怎么办 鼻炎感冒了鼻塞怎么办 左边鼻子不通气怎么办 婴儿鼻塞不通气怎么办 过敏性鼻炎一直打喷嚏怎么办 一只鼻子流鼻涕怎么办 一个鼻子不通气怎么办 夏天鼻子堵了怎么办 做完鼻子感冒了怎么办 鼻子不通流鼻涕打喷嚏怎么办