2015年(123)
分类: 虚拟化
2015-06-21 11:44:39
原文地址:qemu-kvm虚拟机live迁移源代码解读 作者:embeddedlwp
首先,使用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(¶ms)
(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_setup,ram_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_iterate或ram_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 *)
调用se->ops->save_live_complete(f, se->opaque){save_live_complete = block_save_complete或raw_save_complete}
调用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) 循环,获取(load)savevm section 标识,
(4) 如果是扇区开始:
获取(load)section_id,instance_id,version_id
给LoadStateEntry *le分配空间,并赋值其成员,将当前le插入loadvm_handlers链表
并加载savevm状态
调用vmstate_load(f, le->se, le->version_id)
回到(3)。
(5) 如果是扇区结束标识:
获取(load)section_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