Chinaunix首页 | 论坛 | 博客
  • 博客访问: 127073
  • 博文数量: 25
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 251
  • 用 户 组: 普通用户
  • 注册时间: 2014-04-29 14:18
个人简介

不以物喜,勿以己悲;乐观向上,持之以恒。

文章分类

全部博文(25)

文章存档

2015年(25)

我的朋友

分类: 云计算

2015-08-27 14:33:51


qmp_migrate命令实现流程
 
迁移发起端:
入口函数qmp_migrate(migration.c)
> qmp_migrate
     |--> migrate_init
     |--> ? tcp_start_outgoing_migration
     |--> ? rdma_start_outgoing_migration
     |--> ? exec_start_outgoing_migration
     |--> ? exec_start_outgoing_migration
     |--> ? unix_start_outgoing_migration
     |--> ? fd_start_outgoing_migration 

函数说明工作如下:
1. 检查是否支持迁移
2. 函数migrate_init,初始化描述迁移的结构MigrationState,此时迁移状态为MIG_STATE_SETUP
3. 假设走tcp方式迁移,那么迁移的执行函数为tcp_start_outgoing_migration
    假设走fd方式迁移,那么迁移的执行函数为fd_start_outgoing_migration,等等。

如果是fd迁移,调用函数路径如下(根据前面的参数说明,会主要讲解这部分流程):
> fd_start_outgoing_migration(migration-fd.c)
     |--> monitor_get_fd:由uri传入迁移的fdname,得到fd
     |--> qemu_fdopen(savevm.c),根据fd申请QEMUFileSocket结构s和QEMUFile结构f,其中s->fd=fd, s->file=f,f->opaque=s,f->ops=&unix_write_ops(只写fd)
     |--> migrate_fd_connect

如果是tcp迁移,调用函数路径如下:
> tcp_start_outgoing_migration(migration-tcp.c) 
     |--> inet_nonblocking_connect(qemu-sockets.c)
               |--> inet_connect_opts(qemu-sockets.c) : 建立socket连接,监听目的端虚拟机
                         |--> inet_connect_addr
                                   |--> qemu_set_fd_handler2
                                             ?-> wait_for_connect:注册的fd_write函数
                                                       |-> tcp_wait_for_connect(callback函数)
                                                                 |--> qemu_fopen_socket(savevm.c)
                                                                 |--> migrate_fd_connect

inet_connect_opts函数
主要工作如下:
1. 建立qemu socket,调用函数qemu_socket
2. 连接socket
3. 注册IO事件,这里主要注册fd_write函数为wait_for_connect,此函数参数参数为connect_state指针,注册的回调函数为tcp_wait_for_connect

接下来,当tcp连接建立完成,会调用回调函数tcp_wait_for_connect
主要工作如下:
1. 连接成功后,打开qemu socket,获得socket fd file,调用函数qemu_fopen_socket
    此函数根据fd申请QEMUFileSocket结构s和QEMUFile结构f,其中s->fd=fd, s->file=f,f->opaque=s,f->ops=&socket_write_ops(只写fd)
2. 准备开始迁移虚拟机,调用函数migrate_fd_connect,此时迁移状态为MIG_STATE_SETUP.

函数migrate_fd_connect
主要工作如下:
1. 设置迁移状态
2. 设置迁移速度等
3. 创建并启动迁移线程,其中线程执行函数为migration_thread(migration.c)

函数migration_thread
主要工作如下:
1. 函数qemu_savevm_state_begin
    调用se->ops->save_live_setup,内存部分被注册为函数ram_save_setup,完成内容如下:
    1.1 申请并初始化一个内存bitmap,用于记录内存的数据变更
    1.2 调用函数migration_bitmap_sync根据内存中的脏数据来更新bitmap信息
2. 设置迁移状态为MIG_STATE_ACTIVE
3. while循环条件为判断状态是否为MIG_STATE_ACTIVE,如果是,就一直迭代执行内存数据拷贝操作
    3.1 迁移失败,状态为MIG_STATE_ERROR
    3.2 迁移成功,状态为MIG_STATE_COMPLETED

拷贝内存数据时,经历了以下2个阶段:
1. 迭代拷贝阶段:
    1.1 qemu_savevm_state_pending --> ram_save_pending,调用migration_bitmap_sync,重新获取bitmap中的脏数据
    1.2 qemu_savevm_state_iterate --> ram_save_iterate,调用函数ram_save_block,将内存中的数据写入buffer中并等待socket传输到目的端,
          这里需要注意的一点是,每次处理数据时长默认小于50ms,因为内存数据拷贝过程中需要加锁,加锁阶段业务的请求不可访问内存!!
2. 拷贝结束阶段:
    进入此阶段的条件是:[真实剩余内存数据大小]  <= [数据传输带宽 * 最大中断业务时间(默认30ms)]
    2.1 qemu_system_wakeup_request:唤醒虚拟机,恢复虚拟机状态
    2.2 vm_stop_force_state:中断源端虚拟机运行,传入参数RUN_STATE_FINISH_MIGRATE
    2.3 qemu_savevm_state_complete --> ram_save_complete:停机期间,调用函数ram_save_block,将最后的所有内存数据写入buffer中并等待socket传输到目的端,
          然后调用函数migration_end,完成迁移过程中申请内存的释放
    2.4 调用回调函数migrate_fd_cleanup:
          2.4.1 关闭迁移线程
          2.4.2 关闭QEMUFile
          2.4.3 如果最终状态不是MIG_STATE_COMPLETED,则调用函数qemu_savevm_state_cancel -->  ram_migration_cancel --> migration_end 释放为迁移申请的内存空间等,
                  然后把状态改成MIG_STATE_CANCELLED

迁移目的端:
入口函数main(vl.c)
工作内容如下:
1. 解析--incoming参数,将目的端的状态设置为RUN_STATE_INMIGRATE
2. 调用函数qemu_start_incoming_migration接收迁移过来的数据

> qemu_start_incoming_migration(migration.c)
     |-- ? tcp_start_incoming_migration
     |-- ? rdma_start_incoming_migration
     |-- ? exec_start_incoming_migration
     |-- ? unix_start_incoming_migration
     |-- ? fd_start_incoming_migration

上述共有5种方式接收数据,以目的端是通过tcp方式接收数据为例说明。
> tcp_start_incoming_migration(migration-tcp.c)
     |--> inet_listen:监听指定的端口
     |--> qemu_set_fd_handler2:注册IO处理事件,并加入到io_handlers链表,此事件执行函数tcp_accept_incoming_migration,此为注册的read函数

> tcp_accept_incoming_migration(migration-tcp.c)
     |--> qemu_accept:接收链接
     |--> qemu_set_fd_handler2:注册IO处理事件,因为这里传入的读写执行函数均为空,表明是要删除上次注册的IO处理事件
     |--> qemu_fopen_socket:此函数根据fd申请QEMUFileSocket结构s和QEMUFile结构f,其中s->fd=fd, s->file=f,f->opaque=s,f->ops=&socket_read_ops(只读fd)
     |--> process_incoming_migration:开启协程,执行函数process_incoming_migration_co,从buffer中读取数据
     |--> closesocket: 迁移结束,关闭套接字

> process_incoming_migration_co(migration-tcp.c)
     |--> qemu_loadvm_state:从buffer中读取数据
     |--> qemu_fclose:关闭打开的socket,并释放QEMUFile结构
     |--> bdrv_invalidate_cache_all:将元数据下刷到磁盘中,qcow2文件格式注册有此函数,实现方式是先close file,然后再open file ^_^|||
     |--> runstate_set(RUN_STATE_PAUSED):将虚拟机状态设置为paused,下一步libvirt会发一个run命令给虚拟机

函数qemu_loadvm_state(savevm.c)
工作内容如下:
1. 初始化loadvm_handlers链表
2. 判断是否支持在线迁移
3. 循环读取当前buffer对应位置的sizeof(uint8_t)个字符,这个是记录的section_type,表示数据接收开始,完成等信息,
    如果section_type!=0,循环就一直进行
4. 循环结束,所有数据接收完毕,调用cpu_synchronize_all_post_init函数同步并初始化所有cpu

第3步的循环内主要调用函数vmstate_load完成数据的读取。
> vmstate_load(vmstate_load)
     |--> vmstate_load_state
               |--> field->info->get,其中get根据field->name被注册成各个函数,最终调用函数qemu_get_byte完成数据获取
               |--> vmstate_subsection_load

> qemu_peek_byte
     |--> qemu_fill_buffer
               |--> f->ops->get_buffer,如果是通过socket接收数据,则被注册为socket_get_buffer函数,最终调用qemu_recv函数获取数据

以上,qemu部分的在线迁移主流程就介绍完毕,还有诸多细节,有用到的地方再细看。



阅读(4228) | 评论(0) | 转发(0) |
0

上一篇:qemu live migration 流程(2) -- 数据迁移

下一篇:没有了

给主人留下些什么吧!~~