发明内容
有鉴于此,本申请所要解决的技术问题是提供了一种KVM虚拟化下虚拟机克隆的方法及装置。
为了解决上述技术问题,本申请开了一种KVM虚拟化下虚拟机克隆的方法,包括:
暂停第一虚拟机并保存所述第一虚拟机的数据至指定内存中;其中,所述数据在所述指定内存中不被释放;
从所述指定内存中获取虚拟机可执行程序的启动参数;
启动所述虚拟机可执行程序实现所述第二虚拟机的克隆。
其中,启动所述虚拟机可执行程序进行所述第二虚拟机的克隆,包括:
创建子进程,子进程调用虚拟机可执行程序;
启动所述虚拟机可执行程序并进行初始化,并在所述初始化完成后得到所述第二虚拟机。
其中,所述创建子进程包括:所述第一虚拟机对应的父进程将所述第一虚拟机的内存地址以及所述第一虚拟机的显存地址继承给所述子进程以使所述第二虚拟机与所述第一虚拟机共享内存与显存。
其中,所述第一虚拟机的数据至少包括:所述第一虚拟机的虚拟机状态信息、以及所述第一虚拟机的内存地址、显存地址;
其中,所述从所述指定内存中获取虚拟机可执行程序的启动参数,包括:
从所述指定内存中获取所述第一虚拟机的状态信息,并更新所述第一虚拟机的状态信息得到所述启动参数。
进一步地,所述方法还包括:获取所述指定内存的地址;将所述指定内存的地址作为新增参数传入所述虚拟机可执行程序的启动参数。
进一步地,所述方法还包括:所述子进程和所述第一虚拟机对应的父进程以写时复制方式共享页表。
进一步地,所述方法还包括:将所述第一虚拟机暂停时未完成的IO输出至存储介质。
进一步地,所述方法还包括:从所述指定内存中获取所述第一虚拟机的数据,恢复所述第一虚拟机。
本申请还公开一种KVM虚拟化下虚拟机克隆的方法,包括:
暂停第一虚拟机并保存所述第一虚拟机的数据至指定内存中;其中,所述数据在所述指定内存中不被释放;
启动虚拟机可执行程序实现所述第二虚拟机的克隆;
从所述指定内存中将所述数据恢复至所述第一虚拟机。
其中,启动所述虚拟机可执行程序进行所述第二虚拟机的克隆,包括:
创建子进程,子进程调用虚拟机可执行程序;启动所述虚拟机可执行程序并进行初始化,并在所述初始化完成后得到所述第二虚拟机。
其中,所述创建子进程包括:所述第一虚拟机对应的父进程将所述第一虚拟机的内存地址以及所述第一虚拟机的显存地址继承给所述子进程以使所述第二虚拟机与所述第一虚拟机共享内存与显存。
其中,所述第一虚拟机的数据至少包括:所述第一虚拟机的虚拟机状态信息、以及所述第一虚拟机的内存地址、显存地址;
进一步地,所述方法还包括:所述子进程和所述第一虚拟机对应的父进程以写时复制方式共享页表。
进一步地,所述方法还包括:将所述第一虚拟机暂停时未完成的IO输出至存储介质。
本申请还公开一种KVM虚拟化下虚拟机克隆的装置,包括:
第一预处理模块,用于暂停第一虚拟机并保存所述第一虚拟机的数据至指定内存中;其中,所述数据在所述指定内存中不被释放;
第一参数获取模块,用于从所述指定内存中获取虚拟机可执行程序的启动参数;
第一克隆模块,用于启动所述虚拟机可执行程序实现所述第二虚拟机的克隆。
其中,所述第一克隆模块用于:创建子进程,子进程调用虚拟机可执行程序;
启动所述虚拟机可执行程序并进行初始化,并在所述初始化完成后得到所述第二虚拟机。
其中,所述第一克隆模块用于:所述第一虚拟机对应的父进程将所述第一虚拟机的内存地址以及所述第一虚拟机的显存地址继承给所述子进程以使所述第二虚拟机与所述第一虚拟机共享内存与显存。
其中,所述第一虚拟机的数据至少包括:所述第一虚拟机的虚拟机状态信息、以及所述第一虚拟机的内存地址、显存地址;
其中,所述第一参数获取模块用于:从所述指定内存中获取所述第一虚拟机的状态信息,并更新所述第一虚拟机的状态信息得到所述启动参数。
其中,所述第一参数获取模块还用于:获取所述指定内存的地址;将所述指定内存的地址作为新增参数传入所述虚拟机可执行程序的启动参数。
进一步地,所述装置还包括第一共享模块,所述第一共享模块用于:所述子进程和所述第一虚拟机对应的父进程以写时复制方式共享页表。
进一步地,所述装置还包括第一IO输出模块,所述第一IO输出模块用于:将所述第一虚拟机暂停时未完成的IO输出至存储介质。
进一步地,所述装置还包括第一数据恢复模块,所述第一数据恢复模块用于:从所述指定内存中获取所述第一虚拟机的数据,恢复所述第一虚拟机。
本申请还公开一种KVM虚拟化下虚拟机克隆的装置,包括:
第二预处理模块,用于暂停第一虚拟机并保存所述第一虚拟机的数据至指定内存中;其中,所述数据在所述指定内存中不被释放;
第二克隆模块,用于启动虚拟机可执行程序实现所述第二虚拟机的克隆;
第二数据恢复模块,用于从所述指定内存中将所述数据恢复至所述第一虚拟机。
其中,所述第二克隆模块用于:创建子进程,子进程调用虚拟机可执行程序;启动所述虚拟机可执行程序并进行初始化,并在所述初始化完成后得到所述第二虚拟机。
其中,所述第二克隆模块用于:所述第一虚拟机对应的父进程将所述第一虚拟机的内存地址以及所述第一虚拟机的显存地址继承给所述子进程以使所述第二虚拟机与所述第一虚拟机共享内存与显存。
其中,所述第一虚拟机的数据至少包括:所述第一虚拟机的虚拟机状态信息、以及所述第一虚拟机的内存地址、显存地址;
进一步地,所述装置还包括第二共享模块,所述第二共享模块用于:所述子进程和所述第一虚拟机对应的父进程以写时复制方式共享页表。
进一步地,所述装置还包括第二IO输出模块,所述第二IO输出模块用于:将所述第一虚拟机暂停时未完成的IO输出至存储介质。与现有技术相比,本申请可以获得包括以下技术效果:
1)将第一虚拟机的数据保存至不释放带标记的指定内存,从而实现子进程虚拟机可执行程序的启动参数的快速修改更新;
2)第二虚拟机对应的子进程和所述第一虚拟机对应的父进程以写时复制方式共享页表。
3)通过在子进程中执行虚拟机可执行程序来实现虚拟机的快速克隆,其天然地与原虚拟机基于COW(写时复制)共享内存,快速克隆虚拟机时间在100ms-200ms左右,做到用户无感知。
4)在实现第二虚拟机克隆的同时,保障了第一虚拟机的工作能力。
当然,实施本申请的任一产品必不一定需要同时达到以上所述的所有技术效果。
具体实施方式
以下将配合附图及实施例来详细说明本申请的实施方式,藉此对本申请如何应用技术手段来解决技术问题并达成技术功效的实现过程能充分理解并据以实施。
在一个典型的配置中,计算设备包括一个或多个处理器(CPU)、输入/输出接口、网络接口和内存。
内存可能包括计算机可读介质中的非永久性存储器,随机存取存储器(RAM)和/或非易失性内存等形式,如只读存储器(ROM)或闪存(flash RAM)。内存是计算机可读介质的示例。
计算机可读介质包括永久性和非永久性、可移动和非可移动媒体可以由任何方法或技术来实现信息存储。信息可以是计算机可读指令、数据结构、程序的模块或其他数据。计算机的存储介质的例子包括,但不限于相变内存(PRAM)、静态随机存取存储器(SRAM)、动态随机存取存储器(DRAM)、其他类型的随机存取存储器(RAM)、只读存储器(ROM)、电可擦除可编程只读存储器(EEPROM)、快闪记忆体或其他内存技术、只读光盘只读存储器(CD-ROM)、数字多功能光盘(DVD)或其他光学存储、磁盒式磁带,磁带磁磁盘存储或其他磁性存储设备或任何其他非传输介质,可用于存储可以被计算设备访问的信息。按照本文中的界定,计算机可读介质不包括非暂存电脑可读媒体(transitory media),如调制的数据信号和载波。
如在说明书及权利要求当中使用了某些词汇来指称特定组件。本领域技术人员应可理解,硬件制造商可能会用不同名词来称呼同一个组件。本说明书及权利要求并不以名称的差异来作为区分组件的方式,而是以组件在功能上的差异来作为区分的准则。如在通篇说明书及权利要求当中所提及的“包含”为一开放式用语,故应解释成“包含但不限定于”。“大致”是指在可接收的误差范围内,本领域技术人员能够在一定误差范围内解决所述技术问题,基本达到所述技术效果。此外,“耦接”一词在此包含任何直接及间接的电性耦接手段。因此,若文中描述一第一装置耦接于一第二装置,则代表所述第一装置可直接电性耦接于所述第二装置,或通过其他装置或耦接手段间接地电性耦接至所述第二装置。说明书后续描述为实施本申请的较佳实施方式,然所述描述乃以说明本申请的一般原则为目的,并非用以限定本申请的范围。本申请的保护范围当视所附权利要求所界定者为准。
还需要说明的是,术语“包括”、“包含”或者其任何其他变体意在涵盖非排他性的包含,从而使得包括一系列要素的商品或者系统不仅包括那些要素,而且还包括没有明确列出的其他要素,或者是还包括为这种商品或者系统所固有的要素。在没有更多限制的情况下,由语句“包括一个……”限定的要素,并不排除在包括所述要素的商品或者系统中还存在另外的相同要素。
KVM即Kernel-based Virtual Machine的简称,即基于内核的虚拟机,是一种用于Linux内核中的虚拟化基础设施。KVM的主要功能是实现CPU的虚拟化、内存虚拟化、中断虚拟化等。Linux加载了KVM模块后,因用户无法直接控制内核模块的操作,需进一步利用其它工具创建虚拟机。
QEMU,即Quick Emulator(快速仿真器),是一个广泛使用的开源计算机仿真器和虚拟机。当作为一个虚拟机时,QEMU可以通过直接使用真机的系统资源,让虚拟系统能够获得接近于物理机的性能表现。然而,QEMU独立运行时性能很差,利用KVM通过一些硬件虚拟化功能提供的接口,可以实现运行时的高性能。
在KVM虚拟化中,QEMU是运行在Host(Linux宿主机)上的进程,是虚拟机的容器。虚拟机跑在QEMU进程中,虚拟机每一个VCPU(Virtual CPU)对应QEMU进程的一个线程,虚拟机使用的内存是QEMU进程在宿主机上申请的,除此之外,QEMU进程还负责虚拟机设备(例如网卡、键盘、鼠标)的模拟。以下部分将结合附图对本申请的优选实施例进行详细的阐述。
图1是本申请实施例一的技术流程图,结合图1,本申请一种KVM虚拟化下虚拟机克隆的方法,可以由以下步骤实现:
步骤S110:暂停第一虚拟机并保存所述第一虚拟机的数据至指定内存中;其中,所述数据在所述指定内存中不被释放
步骤S120:从所述指定内存中获取虚拟机可执行程序的启动参数;
步骤S130:启动所述虚拟机可执行程序实现所述第二虚拟机的克隆。
具体的,在步骤S110中,预先修改Linux内核从而在虚拟机克隆之前进行一些预处理以使得在虚拟机克隆调用exec函数时,不释放预先指定的内存。所述指定的内存是QEMU在宿主机host上申请的,并且带有特殊标记,用以作为执行exec调用时不被释放的识别标志。
宿主机分配所述指定内存时,为指定的不释放内存添加识别标志。与此同时,通过修改QEMU的设置,不指定DONTFORK标记从而使得fork()生成的子进程和所述第一虚拟机对应的父进程以写时复制方式共享页表。DONTFORK标记在QEMU中是默认自动添加的,若是内存被添加了这个标记,在创建子进程时,内存不会继承给子进程。本申请中,需要将父进程的内存继承给子进程,从而通过采用这种方式使得新克隆的第二虚拟机和原第一虚拟机可以共享内存。
暂停第一虚拟机,即被克隆的虚拟机,在暂停之后,预先保留暂停时刻第一虚拟机的数据。保留所述数据,目的之一在于从所述数据中获取第一虚拟的状态信息,从而快速更新所述状态信息获得第二虚拟机克隆时,虚拟机可执行程序所需的启动参数;目的之二在于,保留所述第一虚拟机的数据以避免后续执行exec系统调用时丢失这些数据,同时,第一虚拟机恢复运行后根据这些保留的数据还可以恢复到暂停时的状态,在成功克隆出第二虚拟机的同时保证所述第一虚拟机可以正常运行。其中,所述当前数据包括所述第一虚拟机的虚拟机状态信息、以及所述第一虚拟机使用的内存地址、显存地址。所述虚拟机状态为所述第一虚拟机所需要保存状态的所有设备的当前状态信息,例如虚拟机的机名、通用唯一识别码,mac地址、CPU运行状态,鼠标键盘等设备状态。具体的,在步骤S120中,从所述指定内存中获取虚拟机可执行程序的启动参数。本申请实施例中的所述虚拟机可执行程序可以是QEMU可执行程序。
所述QEMU可执行程序在启动之前需要配置一些启动参数,例如VNC(VirtualNetwork Computer,即虚拟网络计算机)地址、虚拟机名、UUID(Universally UniqueIdentifier,即通用唯一识别码)等。
本申请实施例中,QEMU可执行程序的启动参数是依据第一虚拟机的状态信息进行修改更新的。换言之,克隆出的第二虚拟机与第一虚拟机的状态信息类似,第二虚拟机的状态信息只需要通过第一虚拟机的状态进行修改即可快速得到。
例如,第一虚拟机的网卡mac地址为:
mac=52:54:00:ce:67:a9,第二虚拟机的网卡mac地址就可以改为mac=52:54:00:ce:67:aa,这样可以避免产生mac冲突。
需要说明的是,本申请实施例中,还需将带有执行exec时不释放标记的所述指定的内存的地址作为新增参数传入所述QEMU可执行程序的启动参数,其目的在于,子进程执行QEMU可执行程序时,直接根据QEMU可执行程序中的新增参数从所述指定的内存地址中获取所述第一虚拟机的内存地址以及显存地址供第二虚拟机共享。
具体的,在步骤S130中,通过fork()创建代表新虚拟机即第二虚拟机的子进程。
一个进程包括代码、数据和分配给进程的资源,fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。
fork函数的一次调用,会返回两个值。它在调用进程(称为父进程)中返回一次,返回值是新派生进程(称为子进程)的进程ID号;在子进程中又返回一次,返回值为0.因此,返回值本身可以告知当前进程是子进程还是父进程。fork在子进程返回0而不是父进程的进程ID的原因在于:任何子进程只有一个父进程,而子进程总是可以通过getppid取得父进程的进程ID。相反,父进程可以有许多子进程而且无法获取各个子进程的进程ID。如果父进程想要跟踪所有子进程的进程ID,那么它必须记录每次调用fork的返回值。
需要说明的是,fork()函数创建的子进程可与父进程之间共享内存、天然地具有COW(copy on write,即写时复制)特性,同时,父进程中的执行exec系统调用时内存不释放的标识也被继承给子进程。
本步骤中,所述子进程通过调用exec()函数启动所述虚拟机可执行程序。exec族函数,其功能在于根据指定的文件名找到可执行文件。exec系统调用经常与fork()联合使用,先用fork建立一个子进程,然后在子进程中使用exec,这样就实现了父进程运行一个与其不同的子进程,并且父进程不会被覆盖。执行exec系统调用,一般是先用fork()函数新建立一个进程,然后让新建的进程去执行exec调用。在fork()建立新进程之后,父进程与子进程共享代码段,但数据空间是分开的,但父进程会把自己数据空间的内容拷贝到子进程中去,还有上下文也会拷贝到子进程中去。为了提高效率,采用一种写时复制(copy onwrite)的策略,即创建子进程的时候,并不拷贝父进程的地址空间,父子进程拥有共同的地址空间,只有当子进程需要写入数据时(如向缓冲区写入数据,这时候会复制地址空间,复制缓冲区到子进程中去。从而父子进程拥有独立的地址空间。而对于fork()之后执行exec后,这种策略能够很好的提高效率,如果一开始就拷贝,那么exec之后,子进程的数据会被放弃,被新的进程所代替。
上一步骤中,获取到所述虚拟机可执行程序的启动参数,在配置好启动参数之后,所述虚拟机可执行程序即可启动工作并进行初始化。
其中,所述初始化可以包括各种设备的初始化。KVM虚拟化下大多设备都是QEMU软件模拟的。例如,在软件模块,QEUM需要申请各种数据结构用来存放这些设备的状态,申请各种定时器来模拟各种时钟(RTC、ACPI PM Timer等)。
比如VCPU的初始化,在KVM虚拟化下,虚拟机的一个VCPU跑在一个QEMU线程中,QEMU就需要创建线程,申请各种数据结构存放VCPU的状态。由于所述启动参数中包含所述指定的内存地址,因此在给第二虚拟机申请内存时直接使用所述指定的内存中保存的第一虚拟机的内存地址,而不再重新申请,因为内存不释放标志在fork()创建时,由父进程继承给了子进程,因此子进程调用exec执行QEMU可执行程序时,被标记的所述指定内存并没有被释放,可以继续使用,从而实现了第一虚拟机和第二虚拟机的内存共享。
优选的,本申请实施例中,虚拟机可执行程序在初始化完成之后恢复所述预先指定的内存中的所述第一虚拟机当前数据。从而,在克隆出新虚拟机的情况下,完好地保留了原虚拟机的工作能力。
优选的,本申请实施例中,恢复运行的第一虚拟机以及fork-exec后开始运行的第二虚拟机都会以写时复制形式写入到设定的不同路径。假设第一虚拟机原本的数据存储路径为vm1.qcow2,在第一虚拟机暂停之后,写入第一虚拟机和第二虚拟机的数据都将写入新的路径,例如第一虚拟机的数据存储路径可以为vm11.qcow2,第二虚拟机的数据存储路径可以为vm12.qcow2,新路径vm11.cow2以及vm12.cow2都以vm1.qcow2为backing file(后备镜像),采用后备镜像差量生成虚拟机的方式,在多台虚拟机上公用一个后备镜像,节省大量磁盘空间。
优选的,本申请实施例中,进一步还包括,在执行第二虚拟机克隆之前,将第一虚拟机后端未完成的IO强制输出至存储介质,避免所述子进程执行exec系统调用时导致这些未完成的IO丢失。其中,所述存储介质可以是虚拟机的存储介质,可以是本地HOST上的磁盘,也可以是网络的分布式网盘,本申请实施例并不限制。
本实施例中,通过修改Linux内核使得执行exec系统调用时不释放带标记的指定内存,从而实现子进程QEMU可执行程序的启动参数的快速修改更新;通过修改QEMU,不指定DONTFORK标记以使fork()生成的子进程和所述第一虚拟机对应的父进程以写时复制方式共享页表;与此同时,通过在fork()生成的子进程中调用exec()执行QEMU可执行程序来实现虚拟机的快速克隆,其天然地与原虚拟机基于写时拷贝COW共享内存,快速克隆虚拟机vmfork时间在100ms-200ms左右,做到用户无感知。
图2是本申请实施例一对应的装置结构示意图,结合图2,所述装置包括:第一预处理模块210、第一参数获取模块220、第一克隆模块230、第一共享模块240、第一IO输出模块250、数据复原模块260。
所述第一预处理模块210,用于暂停第一虚拟机并保存所述第一虚拟机的数据至指定内存中;其中,所述数据在所述指定内存中不被释放;
所述第一参数获取模块220,用于从所述指定内存中获取虚拟机可执行程序的启动参数;
所述第一克隆模块230,用于启动所述虚拟机可执行程序实现所述第二虚拟机的克隆。
其中,所述第一克隆模块230用于:创建子进程,子进程调用虚拟机可执行程序;
启动所述虚拟机可执行程序并进行初始化,并在所述初始化完成后得到所述第二虚拟机。
其中,所述第一克隆模块230用于:所述第一虚拟机对应的父进程将所述第一虚拟机的内存地址以及所述第一虚拟机的显存地址继承给所述子进程以使所述第二虚拟机与所述第一虚拟机共享内存与显存。
其中,所述第一虚拟机的数据至少包括:所述第一虚拟机的虚拟机状态信息、以及所述第一虚拟机的内存地址、显存地址;
其中,所述第一参数获取模块210用于:从所述指定内存中获取所述第一虚拟机的状态信息,并更新所述第一虚拟机的状态信息得到所述启动参数。
其中,所述第一参数获取模块210还用于:获取所述指定内存的地址;将所述指定内存的地址作为新增参数传入所述虚拟机可执行程序的启动参数。
进一步地,所述装置还包括第一共享模块240,所述第一共享模块240用于:所述子进程和所述第一虚拟机对应的父进程以写时复制方式共享页表。
进一步地,所述装置还包括第一IO输出模块250,所述第一IO输出模块250用于:将所述第一虚拟机暂停时未完成的IO输出至存储介质。
进一步地,所述装置还包括第一数据恢复模块260,所述第一数据恢复模块260用于:从所述指定内存中获取所述第一虚拟机的数据,恢复所述第一虚拟机。
图2所示装置可以执行图1所示实施例所述方法,其实现原理和技术效果不再赘述。
图3是本申请实施例二的技术流程图,结合图3,本申请实施例一种KVM虚拟化下虚拟机克隆装置的另一实施方式包括:
步骤S310:暂停第一虚拟机并保存所述第一虚拟机的数据至指定内存中;其中,所述数据在所述指定内存中不被释放;
步骤S320:启动虚拟机可执行程序实现所述第二虚拟机的克隆;
步骤S330:从所述指定内存中将所述数据恢复至所述第一虚拟机。
上述步骤的执行过程参见图1所示的实施例,此处不再赘述。
需要说明的是,本申请实施例中,虚拟机可执行程序在初始化完成之后得到克隆出的所述第二虚拟机,此时即可恢复所述预先指定的内存中的所述第一虚拟机当前数据。从而,在克隆出新虚拟机的情况下,完好地保留了原虚拟机的工作能力。
图4是本申请实施例二的装置结构示意图,结合图4,所述装置包括:
第二预处理模块410,用于暂停第一虚拟机并保存所述第一虚拟机的数据至指定内存中;其中,所述数据在所述指定内存中不被释放;
第二克隆模块420,用于启动虚拟机可执行程序实现所述第二虚拟机的克隆;
第二数据恢复模块430,用于从所述指定内存中将所述数据恢复至所述第一虚拟机。
其中,所述第二克隆模块420用于:创建子进程,子进程调用虚拟机可执行程序;启动所述虚拟机可执行程序并进行初始化,并在所述初始化完成后得到所述第二虚拟机。
其中,所述第二克隆模块420用于:所述第一虚拟机对应的父进程将所述第一虚拟机的内存地址以及所述第一虚拟机的显存地址继承给所述子进程以使所述第二虚拟机与所述第一虚拟机共享内存与显存。
其中,所述第一虚拟机的数据至少包括:所述第一虚拟机的虚拟机状态信息、以及所述第一虚拟机的内存地址、显存地址;
进一步地,所述装置还包括第二共享模块440,所述第二共享模块440用于:所述子进程和所述第一虚拟机对应的父进程以写时复制方式共享页表。
进一步地,所述装置还包括第二IO输出模块450,所述第二IO输出模块450用于:将所述第一虚拟机暂停时未完成的IO输出至存储介质。
图4所示装置可以执行图3所示实施例所述方法,其实现原理和技术效果不再赘述。
应用实例
以下部分将结合图3,阐述在一个实际的应用场景中,本申请技术方案的实现的完整过程。
根据本申请的技术方案,在进行虚拟机克隆之前,预先修改Linux系统内核,以使系统在调用exec时不释放带指定标记的内存。
1、向宿主机申请一块带指定标记的内存,记为内存M。这块内存在系统执行exec调用时不被释放。此时宿主机在分配内存时,给这块内存添加exec时不释放的标记,同时去除默认的DONTFORK标记,从而这块带标记的内存在fork创建子进程时,由父进程继承给子进程。
2、暂停第一虚拟机的运行,此处所述第一虚拟机是被克隆的虚拟机。
3、将第一虚拟机后端没完成的I/O刷到任意存储介质,系统执行exec调用后除了指定保留的内存,其他的都被释放掉了,包括在后端没完成的I/O的内存中数据结构也没了,这样会导致IO丢失。
4、保存第一虚拟机的状态以及第一虚拟机使用的内存地址核显存地址到预先申请的指定内存M中。系统执行exec调用后除了指定保留的内存,其他的都被释放掉了,因此为了保证第一虚拟机在恢复运行之后的工作状态,需要预先保存第一虚拟机的设备如CPU、磁盘、鼠标等的状态。
5、从第一虚拟机暂停的时间节点开始,所有写入第一虚拟机以及第二虚拟机的数据都将以COW的形式,存放到别的路径,路径的命名可以是以原镜像名—虚拟机命名。
6、设置QEMU申请的用于虚拟机的内存和显存继承给子进程。通过fork创建代表新虚拟机即第二虚拟机的子进程。由fork函数的特性决定,被创建的子进程和父进程之间共享内存,天然地具有COW特性。如图3所示,执行pid=fork(),创建子进程,pid将返回两个值。若pid的返回值为0,则说明当前进程为新创建的子进程,执行步骤7;若pid的返回值非零,则判断当前进程是父进程,直接执行步骤10即可。
7、被创建的子进程调用exec执行QEMU可执行程序并更新QEMU可执行程序的启动参数。具体更新方法是,使用父进程调用QEMU可执行程序时类似的启动参数,并按照需要替换其中的参数,比如vnc地址、虚拟机名,uuid等,除此之外还需将预先申请的指定内存M的地址作为新增参数传入QEMU可执行程序的启动参数中,从而实现对QEMU可执行程序启动参数的更新。
8、QEMU可执行程序的启动参数被更新后,QEMU可执行程序从main处开始运行,进行一系列的初始化。因更新后的启动参数中有指定内存M的地址传入,所以在给第二虚拟机申请内存时,直接使用M中保存的第一虚拟机的内存地址,而不再重新申请,由此实现了新旧虚拟机的内存共享,节省了内存空间。
9、QEMU可执行程序初始化完成后,恢复指定内存M中保存的虚拟机状态。
10、释放预先申请的指定内存M并恢复第一虚拟机和第二虚拟机的运行。刚恢复运行的第一虚拟机和新被克隆出来的第二虚拟机在初始时,具有一样的运行状态。这样一个克隆过程,从第一虚拟机暂停至第一虚拟机恢复运行的时间只需要100ms-200ms,几乎做到了用户无感知,快速克隆的同时实现了良好的用户体验。
上述说明示出并描述了本发明的若干优选实施例,但如前所述,应当理解本发明并非局限于本文所披露的形式,不应看作是对其他实施例的排除,而可用于各种其他组合、修改和环境,并能够在本文所述发明构想范围内,通过上述教导或相关领域的技术或知识进行改动。而本领域人员所进行的改动和变化不脱离本发明的精神和范围,则都应在本发明所附权利要求的保护范围内。