Под капотом · Виртуализация · Создание и запуск

Включение ВМ

Пользователь запускает ВМ, которая уже определена в libvirt. Для этого он вводит команду:

Команда
virsh start vm1
libvirt API virDomainCreate(dom)

После выполнения команды идёт самая длинная цепочка - окружение, QEMU, инициализация KVM, выходы из гостя.

  • пользователь 1. Пользователь вводит команду включения
    virDomainCreate(dom)

    Клиент отправляет один RPC-вызов и ждёт результат - всё остальное делает демон.

  • libvirt 2. Демон готовит окружение
    тома и диски → TAP/bridge → cgroup
    auid/gid → метки SELinux/AppArmor → SMBIOS

    Это та «невидимая» работа, которой нет в аргументах QEMU и которая чаще всего ломается.

  • libvirt 3. Демон запускает процесс QEMU
    fork/exec qemu-system-x86_64 \
      -machine q35 -accel kvm \
      -drive …, -netdev …, -qmp unix:…

    Командная строка собирается из XML; часть ресурсов передаётся уже открытыми файловыми дескрипторами.

  • QEMU 4. QEMU собирает машину
    чипсет, RAM, устройства, прошивка (SeaBIOS/UEFI)

    Один процесс QEMU = одна ВМ. Внутри - основной цикл плюс по потоку на каждый vCPU.

  • KVM 5. QEMU получает доступ к KVM
    open("/dev/kvm")
    KVM_GET_API_VERSION → 12   // иначе userspace обязан отказаться
    KVM_CHECK_EXTENSION(KVM_CAP_*)

    API стабилен с Linux 2.6.22; новые возможности проверяются запросом, а не версией ядра.

  • KVM 6. Ядро создаёт виртуальную машину
    KVM_CREATE_VM            → vm fd
    KVM_SET_USER_MEMORY_REGION → слоты памяти гостя

    У новой VM нет ни памяти, ни процессоров: и то и другое добавляется отдельными шагами.

  • KVM 7. Появляются прерывания и vCPU
    KVM_CREATE_IRQCHIP     // PIC + IOAPIC + LAPIC в ядре
    KVM_CREATE_VCPU × N    → по fd на каждый vCPU

    in-kernel irqchip убирает часть выходов в userspace; kernel-irqchip=split - компромисс между безопасностью и скоростью.

  • QEMU 8. Потоки vCPU входят в главный цикл
    на каждый vCPU:
      mmap(kvm_run)
      loop { ioctl(KVM_RUN) → обработать exit }

    Пока гость делает «разрешённое», поток хоста не выходит из ioctl(KVM_RUN).

  • libvirt 9. Демон проверяет, что это действительно KVM
    {"execute":"qmp_capabilities"}
    {"execute":"query-kvm"}
    ← {"return":{"enabled":true,"present":true}}

    Так отличают аппаратную виртуализацию от эмуляции инструкций (TCG).

  • гость 10. Гость начинает загрузку
    прошивка → загрузчик → ОС

    Первые обращения к эмулируемым устройствам становятся выходами из гостя - QEMU их обслуживает.