1.3 · How the stack works
KVM vs QEMU vs libvirt: Who Does What
The previous article covered the CPU virtualization extension and how KVM drives it.
“What’s the difference between KVM and QEMU?” is one of the most-asked questions in Linux virtualization, and most answers are three definitions in a row. Definitions are forgettable. What sticks is the causal chain: each piece exists because of a specific gap the piece below it leaves open.
So rather than defining all three and hoping the relationships are inferred, this article starts with KVM’s deliberate limitation and follows the consequences upward.
KVM does CPU and memory. That’s the entire scope.
The previous article established what KVM contributes: it drives the CPU’s virtualization extension so guest code executes natively on real cores, trapping only on privileged operations. It also manages guest memory — mapping the guest’s idea of physical addresses onto host pages, and keeping each guest confined to its own.
CPU and memory. Nothing else.
KVM does not know how to be a disk. It has no concept of a network card, a display, a serial port, a USB controller, or a clock. A guest OS booting up expects all of those to exist, and KVM cannot supply a single one.
This is a design decision, not an oversight, and article 1 explained why: because the hypervisor lives inside a full Linux kernel, it inherits everything Linux already does well. Building device emulation into KVM would mean duplicating work the kernel and userspace already handle. So KVM was scoped to the one thing that genuinely requires kernel privilege and hardware cooperation — running guest CPUs — and everything else was left to someone else.
That leaves an obvious gap. A guest kernel boots, probes for a disk, and finds nothing. Something has to answer.
QEMU pretends to be all the devices
QEMU is a userspace program that emulates an entire machine’s worth of devices. When the guest probes for a disk controller, QEMU responds. When the guest writes a byte to a serial port, QEMU receives it. When the guest sends a network packet, QEMU hands it to the host’s network stack.
Every device a guest believes it has is a software model inside QEMU — a piece of C code that behaves the way a real chip would. There is no hardware behind any of it. Article 4 covers how that actually works, since “emulates devices” is doing a lot of quiet work in that sentence.
QEMU can also emulate a CPU. On its own it’s a complete machine emulator: give it no KVM and it will translate guest instructions in software and boot an OS anyway, slowly. But paired with KVM, it hands the CPU and memory job to KVM and keeps only the devices.
That pairing is what a running VM is:
ONE RUNNING VM
┌─────────────────────────────────┐
│ QEMU process (user space) │
│ ┌───────────────────────────┐ │
│ │ emulates: disk, NIC, │ │ ← the "devices" half
│ │ screen, USB, firmware │ │
│ └───────────────────────────┘ │
│ │ opens /dev/kvm │
└─────────┼───────────────────────┘
│ ioctl(KVM_RUN)
┌─────────▼───────────────────────┐
│ KVM (in the kernel) │ ← the "CPU + RAM" half
│ runs guest on real cores │
└─────────────────────────────────┘
Two structural facts follow from this picture, and both matter later.
One VM is one QEMU process. Not a process per guest CPU, not a pool of workers — a single qemu-system-aarch64 process that is the virtual machine. Kill it and the VM is gone. A four-vCPU guest is that one process with four busy threads inside it, each running a loop: enter the guest, run until something traps, handle it, re-enter. Those threads are ordinary Linux threads, which is why pinning them to physical cores is possible at all.
The two halves hand off constantly. KVM runs guest code until the guest touches something KVM can’t handle — which, in practice, is usually a device. Control returns to QEMU, the relevant device model does its work, and QEMU asks KVM to resume. That handoff is article 5.
So KVM and QEMU together are a complete machine. Which raises the next gap: how do you actually start one?
libvirt manages the whole thing so you don’t have to
Here is the problem with QEMU as a user interface. Describing a machine to it means passing every detail on the command line — each disk with its format and bus, each network interface with its model and backend, the firmware paths, the CPU model, the console configuration, the memory layout. A realistic invocation runs to dozens of flags across many lines.
Nobody wants to type that. More importantly, nobody wants to remember it, reproduce it exactly six months later, or hand it to a colleague.
libvirt solves this by making the VM a document rather than a command. You describe the machine once as XML. The libvirt daemon, libvirtd, reads that XML, constructs the corresponding QEMU command line, launches the process, and tracks it from then on.
YOU
│ virsh / virt-manager / Cockpit / virt-install
▼
libvirtd ── reads domain XML, builds the command line
│
│ fork/exec + a QMP control socket
▼
qemu-system-aarch64 ── the VM process
│
│ ioctl on /dev/kvm
▼
KVM in the kernel
libvirt’s scope is broader than launching processes. It defines and manages virtual networks, storage pools, and snapshots. It exposes a stable API, so tooling written against libvirt keeps working across QEMU versions. And it maintains a control channel to each running QEMU — the QMP socket — which is how a running VM can have a disk hot-plugged or a snapshot taken without restarting.
The tools you type are all front-ends to that daemon. virsh is the command-line client and the one this series lives in. virt-manager is a desktop GUI. virt-install creates new VMs. Cockpit provides a web interface. None of them talk to QEMU directly — they all send API calls to libvirtd, which does the work.
The payoff is that the XML is the VM. There’s no hidden state elsewhere. Change a line in the document and you’ve changed the machine. That’s why the domain XML is the central artifact of libvirt work, and it’s the subject that opens the next phase of this series.
The three sentences
Compressed to their essentials:
- KVM runs the guest’s CPU and memory on real hardware at native speed.
- QEMU pretends to be all the devices the guest thinks it has.
- libvirt stores your VM as XML and drives QEMU so you don’t have to.
Those are worth being able to say from memory. But the more useful version is the causal one, because it explains why the stack has this shape at all:
KVM is small because Linux already provides everything except CPU virtualization. That leaves devices unhandled, so QEMU handles them in userspace. QEMU’s resulting interface is unusable by hand, so libvirt turns it into a document and manages it for you.
Remove any layer and something specific breaks. Without libvirt you’re hand-writing enormous command lines. Without QEMU your guest has no disk to boot from. Without KVM everything still works — just slowly, in software.
What this means for the rest of the series
The division of labour is a map for everything that follows.
Storage work — disk formats, snapshots, thin provisioning — is about what QEMU’s virtual disk points at on the host.
Networking work is about what QEMU’s virtual NIC plugs into: a NAT bridge, a LAN bridge, a TAP device.
Performance work splits across the boundary. Replacing emulated devices with VirtIO ones is QEMU-side. Pinning vCPU threads to physical cores is host-scheduler-side, and possible only because those threads are ordinary Linux threads.
Passthrough is the exception that proves the rule: it bypasses QEMU’s emulation entirely and hands a guest real hardware.
Every one of those is a modification to one part of a machine whose shape you now know.
Summary
- KVM handles guest CPU and memory only. It has no device emulation at all, by design, because Linux already provides everything else.
- QEMU fills that gap: a userspace program that emulates disks, NICs, consoles, and firmware. It can emulate a CPU too, but hands that job to KVM when available.
- One running VM is one QEMU process, with one host thread per vCPU.
- libvirt exists because QEMU’s command line is impractical to write by hand. It stores each VM as XML, builds the command line, launches QEMU, and manages it via a control socket.
virsh,virt-manager,virt-install, and Cockpit are all front-ends to thelibvirtddaemon.- The domain XML is the VM — there’s no hidden state elsewhere.
Comments