733 字
4 分钟
未命名
2025-09-01
无标签

你说得非常对!我之前的分析不够准确。热迁移确实不是立即停机的,而是有一个预拷贝 + 停机拷贝的两阶段过程。让我重新分析正确的调用时机:

正确的热迁移停机时机#

1. 热迁移的两个阶段#

阶段 1:预拷贝阶段(VM 继续运行)#

// migration/migration.c:3487-3495
while (migration_is_active()) {
    if (urgent || !migration_rate_exceeded(s->to_dst_file)) {
        // 🔄 持续拷贝脏页,VM继续运行
        MigIterateState iter_state = migration_iteration_run(s);
        if (iter_state == MIG_ITERATE_BREAK) {
            break;  // 脏内存足够少了,进入停机阶段
        }
    }
}

阶段 2:停机拷贝阶段(VM 停止)#

// 只有当脏内存收敛到阈值以下时,才进入completion阶段
migration_completion(s);

2. 脏内存收敛判断逻辑#

// migration/migration.c:3235-3238
if ((!pending_size || pending_size < s->threshold_size) && can_switchover) {
    trace_migration_thread_low_pending(pending_size);
    // 关键:只有脏内存少于阈值时,才调用completion
    migration_completion(s);
    return MIG_ITERATE_BREAK;
}

关键参数:

  • pending_size:待传输的脏页大小
  • s->threshold_size:停机阈值(通常是 64 KB)
  • can_switchover:是否可以切换(没有阻塞操作)

3. 真正的停机时刻#

// migration/migration.c:2806-2813
static void migration_completion(MigrationState *s)
{
    if (s->state == MIGRATION_STATUS_ACTIVE) {
        // 🎯 这里才是真正的停机调用
        ret = migration_completion_precopy(s, &current_active_state);
    }
}

// migration/migration.c:2731
static int migration_completion_precopy(MigrationState *s, ...)
{
    // 🔥 这里才调用VM停止
    ret = migration_stop_vm(s, RUN_STATE_FINISH_MIGRATE);
}

4. 完整的时序流程#

sequenceDiagram
    participant User as 用户
    participant Migration as 迁移线程
    participant VM as 虚拟机
    participant ITS as ITS设备

    User->>Migration: migrate命令
    Note over Migration: 启动迁移线程,VM继续运行
    
    loop 预拷贝阶段
        Migration->>Migration: migration_iteration_run()
        Migration->>Migration: 检查pending_size
        alt pending_size > threshold_size
            Migration->>Migration: 继续拷贝脏页
            Note over VM: VM持续运行,产生新脏页
        else pending_size < threshold_size
            Migration->>Migration: 脏内存收敛,准备停机
        end
    end
    
    Migration->>Migration: migration_completion()
    Migration->>VM: migration_stop_vm()
    Note over VM: VM停止运行
    VM->>ITS: vm_change_state_handler(running=false)
    ITS->>ITS: 保存ITS表到客户机内存
    
    Note over Migration: 传输剩余脏页和设备状态
    Migration->>Migration: 迁移完成

5. 关键时间点#

  • T 0:用户发起迁移,VM 继续运行
  • T 1-Tn:预拷贝阶段,反复传输脏页,VM 持续运行
  • Tn+1:脏内存收敛到阈值以下
  • Tn+2:VM 停机 ← vm_change_state_handler 在这里被调用
  • Tn+3:传输最后的脏页和设备状态
  • Tn+4:迁移完成

6. ITS 表保存的准确时机#

// 只有在脏内存收敛后的停机阶段,才会触发ITS表保存
migration_iteration_run() 
    → pending_size < threshold_size 
    → migration_completion() 
    → migration_completion_precopy() 
    → migration_stop_vm() 
    → vm_stop_force_state() 
    → do_vm_stop() 
    → vm_state_notify(false, RUN_STATE_FINISH_MIGRATE) 
    → vm_change_state_handler()  // ITS表保存

所以你的理解完全正确!VM 并不是立即停机的,而是等待脏内存收敛到足够小的程度(通常几十 KB),才会进入停机阶段。只有在这个停机阶段,vm_change_state_handler 才会被调用,ITS 表才会被保存到客户机内存中。

这种设计确保了:

  1. 最小化停机时间:只在脏内存很少时才停机
  2. 数据一致性:在 VM 停止状态下保存 ITS 表,避免并发修改
  3. 迁移效率:大部分数据在 VM 运行时就已经传输完成