postcopy

postcopy对我来说一直是一个神秘的东西,这次终于有时间仔细研究一下。

所谓postcopy直接来说就是在虚拟机启动后再迁移

正常情况下我们是先把虚拟机的信息拷贝到目的端,包括内存、设备,然后在目的端启动虚拟机。而postcopy引入了一个非常有意思的想法,就是先把虚拟机启动起来,然后再来迁移需要的信息。当然这里最重要的就是内存了。

本小节也是以内存为主线,其余支持postcopy的部分暂且不表。(主要是因为没看)

从用法开始

在探讨细节前,我们先看看是怎么使用postcopy的。总体过程和普通迁移类似,有一点有意思的区别是在启动迁移后,再启动postcopy。

以在monitor中执行迁移为例,需要执行的命令是:

migrate_set_capability postcopy-ram on       # both source and destination
migrate_set_capability postcopy-blocktime on # both source and destination
migrate -d tcp:0:4444
migrate_start_postcopy                       # after first round of sync

其中第一二条命令需要在目的和源端都执行。然后需要在启动迁移后,再开启postcopy。其中建议在运行了一轮迁移后执行。

启动postcopy

执行migrate_start_postcopy命令,作用是将某个变量置为true。

hmp_migrate_start_postcopy
   qmp_migrate_start_postcopy
       atomic_set(&s->start_postcopy, true);

而这个变量设置后,就会在migration_iteration_run判断。当达到条件则会开启postcopy。听着真是一点也不神奇。

postcopy中的交互

神奇的事情这个时候才开始发生 -- postcopy_start()函数隐藏了大部分的秘密。

先来一个简化的代码流程:

貌似还是有点长,稍微总结一下:

  • 进入后虚拟机停掉了

  • 通知目的地那些内从空间是要丢弃的

  • 发送命令LISTNE/RUN给目的地

上面的流程看着已经有点麻烦了,但是这还只是迁移过程中的源端。目的端是如何和源端进行交互的呢?下面用一个小小的流程图解释一下。

目的和源端的交互是通过几个命令来实现的。

  • DISCARD : 通知目的端需要放弃的内存空间

  • LISTEN : 开启一个新的监听线程

  • PACKAGED : 这个命令有点意思,它将一坨东西打包发送

  • RUN : 告诉目的端可以运行了

当目的端收到RUN命令,主线程就会拉起虚拟机执行了。而在LISTEN命令下开启的postcopy_ram_listen_thread就会肩负其接收后续内存的责任。

好玩的是在目的端还用了一个超级简化的状态机来记录postcopy过程的状态变化:incoming_postcopy_state(PostcopyState)

这个状态迁移实在太简单了,变化如下:

userfaultfd

重点在上面都描述完了,这里只提一下这里利用到的一个内核特性userfaultfd。

也即是我们需要通过这个接口来:

  • 告诉虚拟机哪个内存被丢弃

  • 当新内存来到后,填入指定位置

好了,基本讲完了,以后想到新的再来补充。

Last updated

Was this helpful?