Virtualization & Hypervisors
Running a whole computer — its own OS and all — as a program on top of another computer
For thirty chapters we have watched a single operating system perform one long magic trick: it takes one CPU, one block of RAM, and a pile of devices, and it convinces every process that it has the machine to itself. A process believes it owns the CPU (the scheduler timeslices it), believes it owns a huge private memory (virtual memory fakes it), and believes it owns the disk (the file system shares it). The OS virtualizes the machine for its processes. This chapter asks the natural next question: what if we ran the same trick one level up — and virtualized the entire machine, so that a whole operating system becomes just another program?
That is what a hypervisor does. It is a thin layer of software that carves a real physical computer into several virtual machines, each of which looks like a complete, independent computer — with its own BIOS, its own disks, its own network card, and its own operating system that boots up believing it is running on bare metal. On one physical server you can run Linux and Windows side by side, each fully isolated, each unaware the other exists. If an OS is a program that manages processes, a hypervisor is, quite literally, an OS for OSes.
This is not an academic curiosity — it is the foundation the entire cloud is built on. Every EC2 instance, every Azure VM, every droplet you have ever rented is a virtual machine running under a hypervisor on a server you will never see. In this chapter we will build the idea from the ground up: what a VM really is, the two shapes a hypervisor comes in, the hardware puzzle that made x86 famously hard to virtualize, and the CPU, memory, and I/O tricks that finally made virtual machines fast enough to run the world. This opens Part F, and it hands off directly to containers in the next chapter.
What virtualization really is
Start from something you already trust. When your OS runs ten processes, each process gets a virtual CPU (its slice of real CPU time) and a virtual address space (its private-looking memory). The process cannot tell it is sharing; the illusion is complete. Virtualization in this chapter is the very same idea applied to the entire computer instead of to a single process. Instead of faking a CPU and a memory range for a process, we fake a whole machine — processor, memory, firmware, disks, network card — for an operating system.
The faked machine is a virtual machine (VM). Inside it runs a guest operating system, and that guest boots exactly as it would on physical hardware: it probes for CPUs, finds "RAM", detects "disks" and a "network card", loads its drivers, and starts your applications. The guest has no idea any of this is an illusion. The software creating and policing that illusion is the hypervisor, also called the virtual machine monitor (VMM). It sits between the real hardware and the guests, doing for whole operating systems what an OS does for processes: sharing the CPU, partitioning memory, and mediating access to devices.
- Virtual machine (VM)
- A software-defined computer — virtual CPU, memory, firmware, disks, and NIC — that behaves like real hardware.
- Guest OS
- The operating system installed inside a VM; it boots believing it owns a physical machine.
- Host
- The real, physical machine and (for hosted setups) the OS running directly on it.
- Hypervisor / VMM
- The layer that creates VMs and shares the real hardware among them — an "OS for operating systems".
Why go to all this trouble? Three motivations drove virtualization from a 1960s mainframe idea into the beating heart of every data center. Consolidation: most servers sit nearly idle, so packing ten lightly-used VMs onto one physical box turns wasted hardware into utilisation. Isolation: each VM is a sealed box, so a compromised or crashing guest cannot touch its neighbours on the same host. And the cloud: renting out slices of a big machine as independent VMs, billed by the hour, is only possible because the hypervisor can hand each customer what looks like their own private server.
The one-line idea: An OS virtualizes the machine for its processes. A hypervisor virtualizes the whole machine for entire operating systems. Same trick, one level up.
Type-1 vs Type-2 hypervisors
Hypervisors come in two shapes, distinguished by one question: what sits directly on the physical hardware? A Type-1 hypervisor, also called bare-metal, is itself the lowest layer of software — it boots on the raw machine like an operating system would, and the VMs run directly on top of it. There is no general-purpose OS underneath. VMware ESXi, Xen, and Microsoft Hyper-V are Type-1 hypervisors, and this is what every serious cloud and data center runs.
A Type-2 hypervisor, or hosted hypervisor, runs as an ordinary application on top of a normal operating system. You boot macOS or Windows as usual, then launch the hypervisor like any other program, and the VMs run as processes managed by that program with help from the host kernel. VirtualBox, VMware Workstation/Fusion, and QEMU in its plain user-mode form are Type-2. This is what you install on your laptop to run a Linux VM without repartitioning your disk.
The trade-off is the classic one: performance versus convenience. Type-1 has nothing between it and the hardware, so it is faster and gives stronger isolation, but it demands a dedicated machine and careful setup. Type-2 is trivially convenient — it coexists with your everyday OS and files — but every hardware access has to pass through the host kernel as well as the hypervisor, adding overhead. KVM is the interesting hybrid: it is a Linux kernel module that turns the Linux kernel itself into a Type-1 hypervisor, so Linux is simultaneously a normal OS and the bare-metal VMM. This is why most of the public cloud, under the hood, runs KVM.
- Type-1 (bare-metal)
- Hypervisor runs directly on hardware with no host OS beneath it. Fast, strongly isolated. ESXi, Xen, Hyper-V.
- Type-2 (hosted)
- Hypervisor runs as an app on a normal OS. Convenient, more overhead. VirtualBox, VMware Workstation/Fusion.
- KVM
- A Linux kernel module that promotes the Linux kernel into a Type-1 hypervisor — the workhorse of the public cloud.
- QEMU
- A machine emulator often paired with KVM: QEMU emulates the devices, KVM accelerates the CPU.
$ lscpu | grep Virtualization
Virtualization: VT-x # Intel; would say AMD-V on AMD
$ ls /dev/kvm # present when KVM is available and enabled
/dev/kvmRule of thumb: Laptop or dev box → reach for a Type-2 hypervisor. Server, data center, or cloud → it is Type-1 (very often KVM) every time.
The classic challenge: trap-and-emulate
Here is the crux of the whole problem. A guest operating system was written to run in ring 0 — the most privileged CPU mode — because a real OS needs to run privileged instructions: set up page tables, disable interrupts, program the timer, talk to devices. But we cannot actually let the guest have ring 0, because then it would control the real hardware and could trample the hypervisor and the other VMs. Only the hypervisor may sit in ring 0. So the guest must be demoted to a less-privileged ring while still believing it is running privileged. How?
The elegant classical answer is trap-and-emulate. Run the guest at a lower privilege. As long as it executes ordinary arithmetic and memory instructions, it runs directly on the real CPU at full speed. The moment it attempts a privileged instruction, the CPU refuses and traps — exactly like a syscall trap — transferring control to the hypervisor. The hypervisor then emulates that instruction: it works out what the guest was trying to do, does the safe equivalent on the real hardware or on the VM’s virtual state, and returns. The guest never notices it was intercepted; it just sees the expected result. Run privileged operations by trapping them, emulate them safely, resume.
In 1974 Popek and Goldberg made this precise. They proved a CPU architecture is efficiently virtualizable by trap-and-emulate only if every sensitive instruction — one that touches or depends on privileged machine state — is also a privileged instruction, meaning it traps when run in user mode. If that holds, every dangerous operation reliably bounces to the hypervisor, and everything else runs natively. Elegant, provable, done.
Except that classic 32-bit x86 flunked the test, and this is the famous wrinkle. x86 had a set of sensitive instructions that were not privileged: run them in a lower ring and instead of trapping, they silently did the wrong thing. The textbook example is POPF: in ring 0 it can change the interrupt-enable flag, but in a lower ring it just ignores that part with no fault. A guest kernel using POPF to disable interrupts would get no error and no effect — the hypervisor never learns anything happened, and the guest quietly diverges from reality. Because these instructions did not trap, plain trap-and-emulate was impossible on x86, and for years the conventional wisdom was that x86 simply could not be virtualized.
- Privileged instruction
- One that faults (traps) if executed outside ring 0 — the CPU stops it and hands control to the kernel/hypervisor.
- Sensitive instruction
- One that reads or changes privileged machine state, or behaves differently by privilege level.
- Popek–Goldberg theorem
- Trap-and-emulate works cleanly only if every sensitive instruction is also privileged (so all of them trap).
- The x86 gap
- Classic x86 had ~17 sensitive-but-unprivileged instructions (e.g. POPF, SGDT) that misbehaved silently instead of trapping.
Why this matters historically: This single gap is why virtualizing x86 was a hard research problem, not a formality — and why the clever techniques of the next section had to be invented before AMD and Intel eventually fixed it in hardware.
Three ways to solve it: full, para, and hardware-assisted
Three distinct techniques grew up to defeat the x86 problem, and understanding all three explains a decade of virtualization history.
Full virtualization via binary translation was VMware’s original breakthrough (1999) and the one that shattered the "x86 can’t be virtualized" belief. The guest runs completely unmodified — a stock Windows or Linux, none the wiser — but the hypervisor does not let its kernel code run raw. It scans the guest’s kernel instruction stream just ahead of execution and rewrites it on the fly, replacing every dangerous sensitive instruction (including the ones that would not trap) with a safe sequence that calls into the hypervisor. Ordinary user-space code is left untouched and runs at native speed; only privileged guest code pays the translation tax. It worked, and it worked on hardware that was never designed to allow it — an extraordinary engineering feat.
Paravirtualization took the opposite bargain, made famous by Xen (2003). Instead of tricking an unmodified guest, modify the guest so it knows it is virtual and cooperates. The problematic privileged operations are removed from the guest kernel and replaced with explicit hypercalls — deliberate, efficient calls straight into the hypervisor, the VM equivalent of a system call. No instruction-stream scanning, no silent-instruction problem, and often excellent performance. The cost is obvious: you need a modified guest kernel, so it historically worked for open-source Linux but not for a closed OS you could not patch.
Hardware-assisted virtualization is what settled the matter, and it is what your machine uses today. Around 2005–2006 Intel (VT-x) and AMD (AMD-V) added CPU support built specifically for hypervisors. They introduced a new, deeper privilege dimension: the hypervisor runs in root mode, and the guest runs in non-root mode where it can genuinely use ring 0. The guest OS gets to feel fully privileged, but the CPU is configured so that all the sensitive operations — including the old troublemakers — cleanly cause a VM exit that traps to the hypervisor in root mode. In other words, the hardware finally made x86 satisfy Popek–Goldberg. Trap-and-emulate became clean and correct, unmodified guests ran fast, and the elaborate binary-translation machinery was no longer needed. This is why every hypervisor now assumes VT-x/AMD-V and why `lscpu` reporting it is the first thing you check.
- Binary translation
- Rewrite the guest’s privileged instructions on the fly into safe sequences; guest stays unmodified. Early VMware.
- Paravirtualization
- Modify the guest to replace privileged ops with explicit hypercalls to the hypervisor. Classic Xen.
- Hypercall
- A deliberate call from a paravirtualized guest into the hypervisor — the VM analogue of a syscall.
- Hardware-assisted (VT-x / AMD-V)
- CPU adds a root/non-root split so trap-and-emulate works cleanly on unmodified guests. Today’s default.
- VM exit
- The hardware event that transfers control from a running guest to the hypervisor when a sensitive event occurs.
The through-line: Software heroics (binary translation) and cooperation (paravirtualization) bridged the gap until the CPU vendors closed it in silicon. Today the base CPU problem is solved in hardware — the remaining battle, in the next sections, is memory and I/O speed.
Memory virtualization: two levels of page tables
Recall virtual memory: the guest OS maintains page tables that translate its processes’ virtual addresses into what it believes are physical addresses, and the hardware MMU walks those tables on every access. But inside a VM, the guest’s "physical" memory is itself a fiction the hypervisor invented. The guest’s "physical address 0" is not real RAM address 0 — it is somewhere in a region the hypervisor handed out. So there are now two translations to do: guest-virtual to guest-physical (what the guest thinks it is doing) and guest-physical to host-physical (the mapping only the hypervisor knows). The MMU, however, natively walks only one set of tables. Reconciling that is the memory-virtualization problem.
The first solution, used before hardware help, was shadow page tables. The hypervisor watches the guest’s page tables and secretly builds its own "shadow" tables that collapse both steps into a single guest-virtual to host-physical mapping — and it points the real MMU at the shadows. Translations then run at full hardware speed. The catch is upkeep: every time the guest edits its own page tables, the hypervisor must trap that change and patch the shadows to match. On a busy guest that churns mappings, the storm of traps and the memory cost of duplicate tables made this the single biggest source of virtualization overhead.
Then the CPU vendors solved this too, with a second layer of page tables in hardware: Intel calls it EPT (Extended Page Tables), AMD calls it NPT (Nested Page Tables). Now the MMU walks both levels itself. The guest freely manages its own page tables (guest-virtual to guest-physical) with no hypervisor involvement, and a separate set of tables owned by the hypervisor maps guest-physical to host-physical. On a miss the hardware performs a two-dimensional page walk, chaining through both. The hypervisor no longer traps every guest page-table edit, which removes the dominant overhead of the shadow era. The price is that a full walk touches more table entries, so the TLB — which caches finished translations — matters even more inside a VM than outside it.
- Guest physical address
- The "RAM address" the guest believes it is using — actually a hypervisor-managed abstraction, not real RAM.
- Shadow page tables
- Hypervisor-built tables merging guest-virtual → host-physical directly; correct but expensive to keep in sync.
- EPT / NPT
- Hardware second-level page tables (Intel EPT, AMD NPT) that map guest-physical → host-physical automatically.
- Two-dimensional page walk
- The MMU chaining through both the guest and the hypervisor tables to resolve one address.
Pattern spotting: Notice the recurring move: virtualization keeps adding a level of indirection (here, a second page-table layer), and hardware keeps stepping in to make that extra level cheap. It happened for the CPU with VT-x; it happened for memory with EPT/NPT.
I/O virtualization: the performance ladder
CPU and memory are only half the machine; a VM also needs disks and networking, and I/O virtualization is where the sharpest performance choices live. There is a ladder of approaches, trading speed against portability and simplicity.
The bottom rung is device emulation. The hypervisor pretends to be a real, well-known piece of hardware — say an Intel e1000 network card or an IDE disk — so the guest can use its existing stock driver with no changes at all. Maximum compatibility: any OS that has an e1000 driver just works. But it is slow, because the guest driver pokes at what it thinks are hardware registers, and every one of those pokes traps into the hypervisor to be emulated in software. A single packet can cost many VM exits. Wonderful for booting an old OS, poor for a busy server.
The middle rung is paravirtualized I/O, and on Linux the standard is virtio. Instead of imitating real hardware, the hypervisor exposes a device explicitly designed for virtualization, and the guest loads a matching virtio driver that knows it is talking to a hypervisor. The two sides share a ring buffer in memory and batch many requests per notification, so instead of trapping per register access they cross the boundary rarely, moving data in bulk. virtio-net and virtio-blk are the default disks and NICs in essentially every cloud VM because they hit the sweet spot of good speed with easy portability and migration.
The top rung is passthrough, and its refinement SR-IOV. Here the hypervisor assigns a real physical device — a GPU, an NVMe drive, a NIC — directly to one guest, which drives the actual hardware with near-native performance and almost no hypervisor involvement on the data path (the IOMMU makes this safe by confining the device’s DMA to that guest’s memory). Plain passthrough dedicates the whole device to one VM. SR-IOV goes further: a single physical NIC advertises many lightweight virtual functions, so one card can be passed through to many VMs at once, each getting a near-direct slice. The cost of climbing this high is flexibility — a VM bound to specific physical hardware is far harder to snapshot or live-migrate, which is exactly what the next section is about.
- Emulated device
- Hypervisor imitates real hardware so stock guest drivers work; maximum compatibility, slowest (traps per access).
- virtio
- The standard Linux paravirtual I/O interface; guest and host share ring buffers and batch requests. The cloud default.
- Passthrough
- A physical device assigned directly to one guest for near-native speed; the IOMMU keeps its DMA contained.
- SR-IOV
- One physical device (often a NIC) exposing many virtual functions, so it can be passed through to many VMs at once.
- IOMMU
- Hardware that translates and restricts a device’s memory accesses — what makes safe passthrough possible.
Engineer’s takeaway: When a cloud VM’s disk or network feels slow, the question is which rung you are on. Confirm virtio (not an emulated e1000/IDE) is in use; reserve passthrough/SR-IOV for latency-critical workloads where you can accept losing easy migration.
VMs as files: snapshots, cloning & live migration
Because a VM is entirely software, its whole state is just data — and that turns out to be its superpower. A VM’s virtual disk is a file (a qcow2 or vmdk image); its configuration is a file; and even its live running state — the contents of its virtual RAM and CPU registers at an instant — can be written out to a file. The entire machine is encapsulated as data you can copy, store, and move. Nothing about a physical server works this way.
From encapsulation, three powerful capabilities fall out almost for free. A snapshot captures the complete state of a VM at a moment in time — disk, memory, and CPU — so you can freeze a machine, make risky changes, and roll straight back to the frozen instant if things go wrong, as if the changes never happened. Cloning duplicates a VM’s files to spin up an identical copy in seconds, so a hand-tuned "golden image" becomes the template for a hundred identical servers. And live migration moves a running VM from one physical host to another with essentially no downtime: the hypervisor copies the guest’s memory pages across the network while the VM keeps running, re-copies the handful of pages that changed during the copy, and then, in a final sub-second pause, transfers the last bits of state and CPU registers and resumes the guest on the new host. Users mid-request barely notice.
These are not conveniences bolted on — they are why the cloud economically works. Live migration lets a provider evacuate every VM off a physical server to patch its firmware or replace failing hardware without customers ever seeing an outage. Cloning and templating are how a fresh instance boots in seconds instead of hours. Snapshots underpin backups and quick recovery. Encapsulating a machine as movable, copyable files is the quiet foundation beneath the entire elastic, self-healing cloud.
- Encapsulation
- A whole VM — disk, memory, CPU state, config — represented as files that can be copied, stored, and moved.
- Snapshot
- A saved point-in-time state of a VM you can roll back to; the basis of easy experimentation and backups.
- Cloning / templating
- Duplicating a VM image to launch identical instances in seconds from a golden template.
- Live migration
- Moving a running VM between physical hosts by pre-copying memory, with only a sub-second final switchover.
Why the cloud needs this: A provider can patch hardware, rebalance load, and survive failing machines invisibly — because a running VM is just state that can be picked up and set down on another host.
Why engineers care — and the road to containers
Even if you never configure a hypervisor by hand, virtualization is already under everything you build, and knowing it changes how you reason. Every cloud instance you rent — EC2, Compute Engine, Azure VM, a droplet — is a virtual machine on some provider’s Type-1 hypervisor; the "server" you SSH into is a guest OS, and the performance quirks of the previous sections (virtio versus emulation, EPT costs, "noisy neighbours" sharing your host) are your quirks. When you fire up a local VM to reproduce a Linux-only bug from your Mac, or your CI runs each build in a fresh VM, that is this chapter at work. And virtualization is a genuine security boundary: because a VM cannot see outside its sealed machine, running untrusted or multi-tenant workloads in separate VMs is a strong isolation guarantee — the hypervisor’s attack surface is far smaller than a shared OS.
That isolation is exactly the strength — and the cost. A VM carries a complete operating system: its own kernel, its own boot sequence, gigabytes of disk, hundreds of megabytes of RAM before your app even starts, and tens of seconds to boot. If you want ten copies of a small service, ten full guest OSes is a lot of duplicated machinery. This is the tension that sets up everything ahead: strong isolation buys you safety but costs you weight. VMs sit firmly at the heavy-but-safe end of that spectrum.
- Cloud instances are VMs — your production servers run inside a hypervisor you never see.
- Reproducible dev and CI environments are VMs (and, increasingly, containers) capturing a known-good machine.
- A VM is a real security isolation boundary: separate guests cannot read each other’s memory or state.
- The trade-off to remember: full VM = strong isolation but a whole extra OS of overhead.
Which raises the question the next chapter answers. What if you do not need a separate kernel for every workload — what if isolation at the process level, sharing the host’s one kernel, were enough? Then you could drop the whole duplicated OS, boot in milliseconds instead of seconds, and pack far more workloads onto a machine. That is precisely the bet containers make: lighter-weight isolation built directly on OS features (namespaces and cgroups) rather than on a virtual machine. They trade some of the VM’s hard hardware-level isolation for enormous gains in speed and density.
What is next: We have virtualized the whole machine, kernel and all. Next we ask how little we can virtualize and still get useful isolation — and arrive at containers, the technology behind Docker and Kubernetes and the shape of modern deployment.