首先,使用kvm正常打开一个虚拟机:sudo kvm ./xp1.qcow2

然后,再使用kvm命令打开同一个虚拟机。使用如下命令:sudo kvm ./xp1.qcow2 -incoming tcp:0.0.0.0:11111

读者可能会说,两台虚拟机同时使用同一个image,会造成image数据的丢失,可能会彻底破坏整个虚拟硬盘的数据完整性,从而造成数据丢失,甚至操作系统都无法启动!

是的,但这里的-incoming选项,实际上并没有真正启动虚拟机。它首先创建TCP等链接,准备接受虚拟机迁移的数据传入,然后就暂停了虚拟机的执行。直到虚拟机迁移完成后,才会恢复进入虚拟机运行状态。因此,它在迁移完成前,并没有操作虚拟磁盘,因此不会造成如上的问题。


qemu-kvm:versoin = 1.2.0


源端:

migrate命令会调用hmp_migrate()函数{defined in hmp.c}


hmp_migrate()会调用qmp_migrate(...)函数{defined migrate.c}

(1) 判断当前migrate状态是否为active;此时迁移状态应该为MIG_STATE_SETUP

(2) 判断是否有block migrate设备存在;

(3) 初始化;

      调用migrate_init(&params)

(4)  判断migrate协议:TCP/UNIX/EXEC/FD 开始迁移。

       调用 tcp_start_outgoing_migration(s, p, errp) {p:=host_port}

            exec_start_outgoing_migration(s, p)

            unix_start_outgoing_migration(s, p)

            fd_start_outgoing_migration(s, p)

在这里,假设用的是tcp协议。


tcp_start_outgoing_migration(s, p, errp){defined in migration-tcp.c}

(1)  MigrationState中的函数指针赋值

(2)  调用inet_connect(...)连接监听目的虚拟机

(3)  创建迁移处理线程

      调用migrate_fd_connect(MigrationState *s


migrate_fd_connect(MigrationState *s{defined in migration.c 真正的迁移方法}

(1) 当前迁移状态设为MIG_STATE_ACTIVE

(2) 定制QEMUFile文件

     调用qemu_fopen_ops_buffered(...)返回QEMUFile

          1) QEMUFileBuffered初始化

          2) 定制QEMUFile文件

                调用qemu_fopen_ops(...)返回QEMUFile

(3) 调用qemu_savevm_state_begin(QEMUFile, const MigrationParams)

          初始化 se->ops->save_live_setup block_save_setupram_save_setup)

(4) 调用migrate_fd_put_ready(MigrationState *)

     遍历实现设备内存状态保存


 

migrate_fd_put_ready(MigrationState *)

(1) 迭代预拷贝Iterative Pre-Copy{defined in the file savevm.c}

调用qemu_savevm_state_iterate(QEMUFile *)

          1)遍历实现设备内存状态保存

               调用se->ops->save_live_iterate(f, se->opaque){等于block_save_iterateram_save_iterate}

(2) 唤醒虚拟机状态

     调用等于qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER)

(3) 调用vm_stop_force_state(RUN_STATE_FINISH_MIGRATE)

     1) 终止虚拟机的运行

          调用vm_stop(RUN_STATE_FINISH_MIGRATE)

(4) 停机拷贝,保存新的数据{defined in the file savevm.c}

调用qemu_savevm_state_complete(QEMUFile *)

  1. 调用se->ops->save_live_complete(f, se->opaque){save_live_complete = block_save_completeraw_save_complete}

  2. 调用vmstate_save(…)完成停机拷贝

(5) 迁移完成,提交信息{defined in migration.c}

     调用migrate_fd_completed(MigrationStateb *)

          1) 迁移完成,释放资源

                    migrate_fd_cleanup(MigrationState *)

          2) 释放资源成功,设置迁移状态:= MIG_STATE_COMPLETED

                    s->state = MIG_STATE_COMPLETED;

          3) 否则,s->state = MIG_STATE_ERROR

(6) 判定迁移状态是否完成,s->state == MIG_STATE_COMPLETED or not。完成,则迁移结束;否则,重新运行源VM

 


目的端:

调用main(...) {defined in vl.c}

(1) 解析命令行参数,初始化等

(2) 判断是否有待迁移虚拟机

     调用qemu_start_incoming_migration(incoming, &errp)

          1) 选择KVM支持的迁移通道

               调用 tcp_start_incoming_migration(p, errp)

                     exec_start_incoming_migration(p)

                     unix_start_incoming_migration(p)

                     fd_start_incoming_migration(p)


tcp_start_incoming_migration(const char *host_port, Error **errp) {defined in migration-tcp.c}

(1) 打开监听

     调用inet_listen(...)

(2) 调用qemu_set_fd_handler2(...)

     注册IO事件,加入到io_handlers链表中,并将IO事件的函数指针初始化,尤其是ioh->fd_read = fd_read = tcp_accept_incoming_migration



tcp_accept_incoming_migration (void *opaque) {defined in migration-tcp.c}

(1) 接受连接

     调用qemu_accept(...)

     调用accept(...)

(2) 接受迁移,打开套接子

     调用qemu_fopen_socket(int ){defined savevm.c}

     通过调用qemu_fopen_ops(s, NULL, socket_get_buffer, socket_close, NULL, NULL, NULL)注册文件套接字QEMUFileSocket

(3) 进程接受迁移

     调用process_incoming_migration(QEMUFile *)

(4) 迁移结束相关操作


process_incoming_migration(QEMUFile *) {defined in migration.c}

(1) 导入状态,从套接字中获取VM状态

     调用qemu_loadvm_state(f)

          se->ops->load_state()

(2) qemu_announce_self ()

(3) bdrv_clear_incoming_migration_all ();

(4) bdrv_invalidate_cache_all ()

(5) 如果成功:vm_start();若失败,停止runstate_set()


qemu_loadvm_state(QEMUFile *f){ defined in savevm.c}

(1) 声明loadvm_handlers链表,并初始化,注册LoadStateEntry *le

(2) 判断是否有迁移,没有则return -EINVAL;否则,继续

(3) 循环,获取(loadsavevm section 标识,

(4) 如果是扇区开始:

     获取(loadsection_idinstance_idversion_id

     LoadStateEntry *le分配空间,并赋值其成员,将当前le插入loadvm_handlers链表

     并加载savevm状态

          调用vmstate_load(f, le->se, le->version_id)

     回到(3)

(5) 如果是扇区结束标识:

     获取(loadsection_id,遍历loadvm_handlers,寻找扇区section_id

     并加载savevm状态

          调用vmstate_load(f, le->se, le->version_id)

     回到(4)

(6) 同步所有cpu,初始化

(7) 注销loadvm_handlers,释放LoadStateEntry *le

(8) 成功返回0,失败返回<0


注意:所有支持虚拟机活迁移的虚拟设备,都需要调用register_savevm_live方法,提供保存状态的SaveVMHandlers*save_live_iterate函数,供活迁移开始时被调用 。正是因为块设备注册了SaveStateEntry对象,才使KVM能够支持image不共享的活迁移。

块设备live迁移初始化:

     调用blk_mig_init(void) 

参考:   http://www.cnblogs.com/armlinux/archive/2011/05/10/2390904.html

            http://blog.csdn.net/chenglinhust/article/details/8808731

            http://blog.csdn.net/chenglinhust/article/details/8703131

迭代预拷贝:http://yang19890314.blog.51cto.com/1620466/1163624

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐