The Hardware an OS Drives
The CPU, memory, interrupts and devices every OS abstraction is built on
An operating system is not an abstract idea floating above your programs — it is software written to drive a specific, physical machine. Almost every clever thing the OS does later in this course is really the OS using a hardware feature the way its designers intended. Protection between processes is a CPU mode bit. Preemptive scheduling is a timer chip firing an interrupt. Fast programs are programs that respect the cache. If you do not know the hardware, the OS looks like magic; once you do, it looks like careful engineering.
So before we talk about processes, threads, virtual memory or file systems, we take a tour of the machine underneath. We will look at the CPU and its registers, the privilege levels baked into the silicon, the memory hierarchy from registers down to disk, the interrupt and trap mechanism that lets the outside world grab the processor, the timer that keeps the OS in charge, and the devices, buses and DMA engines that move data around.
This chapter is deliberately concrete. Throughout, we will point at the software-engineering payoff: why a context switch is expensive, why a system call is not free, why cache-friendly data layout can make code an order of magnitude faster, and why a database buffer pool is really a hand-rolled memory hierarchy. Learn the hardware once, and the rest of the course has something solid to attach to.
Why an OS engineer must know the hardware
The entire reason an operating system exists is to manage hardware and hand it to programs in a safe, shared, convenient form. That means every abstraction we will build later maps back to something physical. A process is the CPU plus a slice of memory, guarded. Virtual memory is a hardware address-translation unit plus some tables. A file is bytes on a disk reachable through a device controller. The OS is the layer that turns raw parts into clean ideas.
This matters for you as an engineer even if you never write kernel code. When a service is slow, a server thrashes, or latency spikes under load, the explanation almost always lives at the hardware boundary — a cache miss, a page fault, a syscall storm, an I/O wait. Engineers who can see the hardware through the abstraction can reason about these problems; everyone else reboots and hopes.
- The CPU executes instructions and enforces protection via a privilege mode bit.
- Memory comes in a hierarchy: tiny and fast at the top, huge and slow at the bottom.
- Interrupts and a timer let devices and the clock take control away from a running program.
- Buses, controllers and DMA move bulk data without burning CPU time.
The tour: Keep one question in mind: which hardware feature makes each OS trick possible? Every section answers it.
The CPU & its registers
At its core the CPU does one thing, over and over, billions of times a second: the fetch-decode-execute cycle. It fetches the next instruction from memory, decodes what that instruction means, executes it, and repeats. Which instruction is "next" is held in a special register called the program counter (the PC, sometimes called the instruction pointer). Advance the PC and you move through the program; overwrite it and you jump elsewhere.
The CPU does not reach into RAM for every value it touches — that would be far too slow. Instead it works out of registers: a small set of tiny, blazingly fast storage slots physically on the chip. There are general-purpose registers for your data, and special-purpose ones with fixed jobs: the program counter, the stack pointer (SP), and a status/flags register recording things like whether the last result was zero or overflowed.
- Program counter (PC)
- Holds the address of the next instruction to run; it is the CPU’s bookmark in your code.
- General registers
- A handful of fast slots (e.g. RAX, RBX on x86-64) the CPU computes with directly.
- Stack pointer (SP)
- Points at the top of the current call stack, where locals and return addresses live.
- Status / flags register
- Records side effects of operations — zero, carry, overflow, sign — used by branches.
Here is the preview that makes registers matter for the OS. All the "state" of a running program that is not in memory lives in these registers. So when the OS wants to pause one program and run another, it must save every register somewhere and load the next program’s saved registers back in. That save-and-restore is the essence of a context switch — and because it touches the CPU’s most intimate state, it is not cheap.
Preview: A context switch is, at heart, "dump all registers, load the other set". Remember this when we ask why switching threads too often wrecks performance.
User mode vs kernel mode (privilege rings)
How does the OS stop one buggy program from halting the machine or reading another program’s secrets? Not by good manners — by hardware. The CPU runs in one of (at least) two privilege levels, tracked by a mode bit. In kernel mode the CPU will execute any instruction, including the dangerous ones. In user mode a whole class of privileged instructions is simply forbidden; attempt one and the CPU refuses and traps into the OS.
On x86 these levels are drawn as rings: ring 0 is the fully privileged kernel, ring 3 is where all your applications live, and the outer ring cannot do things like halt the CPU, mask interrupts, change the page tables, or talk to devices directly. Those powers are reserved for the kernel. Most OSes use only ring 0 and ring 3 and ignore the two in between.
- Mode bit
- A single hardware flag saying whether the CPU is currently privileged (kernel) or restricted (user).
- Ring 0 (kernel)
- Full privilege: all instructions, all memory, all devices. The kernel runs here.
- Ring 3 (user)
- Restricted: no privileged instructions, no direct hardware. Every normal app runs here.
- Privileged instruction
- An instruction (halt, set page table, I/O, disable interrupts) legal only in kernel mode.
This single hardware feature is what makes protection possible, and it is exactly the user/kernel boundary from the introduction. Your program cannot touch hardware because the CPU physically will not let it in user mode; it must ask the kernel via a system call, which is the controlled way to flip into kernel mode, do the privileged work, and flip back. That mode switch — plus the cache and pipeline disruption around it — is why a syscall costs meaningfully more than a plain function call.
Why syscalls cost: A syscall crosses the mode boundary and disturbs caches and the pipeline. That is why high-throughput code batches I/O instead of making a syscall per byte.
The memory hierarchy
There is no single kind of memory that is fast, huge and cheap all at once — physics and economics forbid it. So computers stack several kinds into a hierarchy. At the top sit the CPU registers: a few hundred bytes, effectively instant. Below them are the L1, L2 and L3 caches, then main memory (RAM), then persistent storage (SSD, and slower still, spinning disk). As you go down, each level is bigger and cheaper per byte, but dramatically slower.
The whole scheme works because of locality. Programs tend to reuse the same data and instructions (temporal locality) and to touch data near what they just touched (spatial locality). Hardware exploits this by pulling data into cache in chunks called cache lines, betting you will want the neighbours too. Good code plays along; bad code fights it.
- CPU register — under 1 nanosecond, the CPU’s working set.
- L1 cache — about 1 ns; L2 cache — a few ns; L3 cache — around 10-20 ns.
- Main memory (RAM) — roughly 100 ns, about 100x slower than L1.
- SSD read — tens of microseconds; spinning disk seek — several milliseconds.
- Rule of thumb: each step down is 10x-100x slower — RAM to disk spans a factor of tens of thousands.
This is where hardware knowledge turns into faster software. Iterating an array in order is fast because it walks along cache lines; hopping randomly through a linked list or a pointer-chasing tree stalls the CPU waiting on RAM. Packing hot fields together, choosing arrays over scattered objects, and keeping the working set inside cache are not micro-optimisations — they routinely make real code several times faster.
Engineer’s aside — buffer pools: A database buffer pool is a memory hierarchy built by hand: it keeps hot disk pages in RAM so most reads never touch the slow disk. Same principle as a CPU cache, one level up.
Interrupts & traps
If the CPU only ever ran straight through your program, it could never notice that a key was pressed, a packet arrived, or a disk finished a read. The mechanism that lets the outside world grab the processor is the interrupt. A device raises an electrical signal; the CPU finishes its current instruction, saves just enough state, and jumps to a predefined handler — then resumes what it was doing as if nothing happened.
How does the CPU know where to jump? Through the interrupt vector: a table, set up by the OS at boot, mapping each interrupt number to the address of its handler routine. Interrupt 14 might be a page fault, another number the keyboard, another the timer. The CPU indexes the table and dispatches. This is why we say an OS is fundamentally interrupt-driven: for most of its life the kernel is idle, waiting, and springs to action only when an interrupt or trap fires.
- Hardware interrupt
- An asynchronous signal from a device (disk, NIC, keyboard, timer) — arrives whenever the device is ready.
- Trap (software interrupt)
- A deliberate, synchronous jump into the kernel triggered by the program itself — how a system call is made.
- Exception / fault
- An interrupt the CPU raises on an error mid-instruction — divide by zero, or a page fault the OS then fixes up.
- Interrupt vector
- The table mapping each interrupt number to its handler address; the OS fills it in at boot.
One mechanism, three uses: Devices (async), syscalls (deliberate traps) and errors (faults) all funnel through the same "stop, save, jump to handler" machinery. Learn it once and three topics fall into place.
The clock / timer
Here is a puzzle: once the OS hands the CPU to your program and switches to user mode, how does it ever get control back? Your program is running, not the kernel. If your code entered an infinite loop, what stops it from owning the CPU forever? The answer is a dedicated piece of hardware — the programmable timer — that fires an interrupt at a fixed, regular interval, whether the running program likes it or not.
Each of these ticks (historically the "timer interrupt" or "tick", often every few milliseconds) drags the CPU out of user mode and into the kernel’s timer handler. At that moment the OS is back in charge and can make a decision: has this process had enough time? If so, it saves the process’s registers, picks another, and returns to that one instead. That forced hand-back is exactly what makes preemptive multitasking possible.
- The timer fires periodically regardless of what user code is doing — the OS cannot be starved out.
- Every tick is a chance for the scheduler to preempt the current process and switch to another.
- The tick interval is a tradeoff: shorter means snappier switching but more overhead spent in the handler.
- Without the timer there is no preemption — a single loop could freeze the whole machine.
Ties to scheduling: The timer interrupt is the heartbeat of the scheduler. When we study scheduling, this tick is the moment every "who runs next?" decision is made.
I/O devices, controllers & buses
The CPU does not talk to a disk or a network card directly. Between them sits a device controller — a small dedicated chip that knows the messy details of one device and exposes a tidy set of device registers: typically a status register (is the device ready or busy?), a command register (what should it do?), and a data register (the bytes moving through). The OS driver drives the device entirely by reading and writing these registers.
There are two ways the CPU reaches those registers. With memory-mapped I/O, the device registers are wired into ordinary memory addresses, so a plain load or store to a special address talks to the device. With port-mapped I/O, the device lives in a separate address space accessed by dedicated IN/OUT instructions. Modern hardware leans heavily on memory-mapped I/O. Tying it all together is the bus — the shared set of wires over which the CPU, memory and controllers exchange addresses and data.
- Device controller
- The chip that operates one device and presents it to the CPU as a handful of registers.
- Device registers
- The status, command and data slots the driver reads and writes to control the device.
- Memory-mapped I/O
- Device registers appear at normal memory addresses; ordinary loads/stores talk to the device.
- Bus
- The shared wires (address + data + control) connecting CPU, memory and controllers.
That leaves one question: after the driver tells a device to do something, how does it learn the work is done? Two styles. In polling, the CPU sits in a loop reading the status register over and over — simple, but it burns the CPU waiting. With interrupts, the driver kicks off the work and moves on; the controller raises an interrupt when finished. Interrupts are why your machine stays responsive during a slow download instead of spinning uselessly.
Polling vs interrupts: Polling wastes the CPU but has low latency for very fast devices; interrupts free the CPU for slow ones. Real drivers mix both (e.g. Linux NAPI polls a busy NIC to avoid interrupt storms).
DMA & the big picture
Even with interrupts, one problem remains: moving a megabyte of data one register-load at a time would still pin the CPU copying bytes for the whole transfer. The fix is Direct Memory Access (DMA). The CPU tells a DMA controller "move this many bytes between this device and this region of RAM", and then walks away to do useful work. The DMA engine performs the whole bulk transfer over the bus by itself.
When the transfer finishes, the DMA controller raises a single interrupt to say "done". So instead of thousands of interruptions or a busy CPU shuffling bytes, the CPU is disturbed exactly once per block. This is how disks and network cards move data at full speed while your programs keep running — the CPU sets up the job and the hardware does the heavy lifting.
- DMA controller
- A hardware engine that transfers blocks of data between a device and RAM without the CPU copying each byte.
- Bulk transfer
- The whole block moves in one operation over the bus, freeing the CPU meanwhile.
- Completion interrupt
- The single interrupt the DMA engine raises when the transfer is finished.
Step back and the whole chapter connects. The CPU and its registers give us execution and, via save/restore, context switches. The mode bit gives us protection and the user/kernel boundary. The memory hierarchy shapes performance. Interrupts, the timer, controllers and DMA give the OS its hands and its heartbeat. Everything ahead — processes, scheduling, virtual memory, files — is the OS wrapping these physical parts in abstractions you can program against without ever touching a device register.
What comes next: From here on we build up: the process, the system call, the scheduler, the page table. Each one is the OS putting the hardware in this chapter to work. Now you know what it is really driving.