JOVANA
Explore Library Glossary Getting Started Three Levels Fields How it works Mission
Join the mission
All guides

Boot, Traps, and the Interrupt Mechanism

A kernel does not start itself, and once running it spends its life being interrupted. This guide follows the machine from the first instruction after power-on to a steady state where every keypress, timer tick, and system call funnels through one elegant mechanism: the trap.

From power-on to the first kernel instruction

Last guide you sorted kernels by shapemonolithic, microkernel, hybrid. This guide asks a more basic question that every shape must answer: how does the thing get into memory and start running at all? When you press the power button, the CPU does not magically find your kernel. It is a piece of silicon that, on reset, knows exactly one thing: a fixed, hard-wired address to fetch its first instruction from. On a small microcontroller that address is the reset vector; on a PC the firmware plays the same role. The point is that the very first code is not chosen by software — it is burned into the hardware, because there has to be a starting point that needs no other software to exist first.

That first code is tiny and its only real job is to load something bigger. This is the bootloader: a small program whose mission is to find the kernel image on a disk or flash chip, copy it into RAM, set up just enough of the machine to run it, and then jump to the kernel's entry point. It is a relay race of trust — firmware hands the baton to the bootloader, the bootloader hands it to the kernel — and each runner does the minimum needed to get the next, more capable one going. The bootloader is not the kernel; it is the thing that makes room for the kernel and then steps aside.

When the kernel's entry point finally runs, the machine is in a rough, half-built state, and the kernel boot sequence is the deliberate work of finishing the house while living in it. It switches the CPU into its full addressing mode, builds the page tables that give it a kernel address space, detects how much RAM exists and which devices are present, brings up the other CPU cores, and — crucially for this guide — installs the table that tells the hardware where to jump when something interrupts. Only after all of that does it start the first user process. Boot is not one instant; it is a careful ordering where each step assumes the previous ones already succeeded.

Three things that look identical to the CPU: trap, fault, interrupt

Once running, a kernel almost never just "runs from top to bottom." It mostly sits idle until something happens, then reacts. The hardware mechanism behind every such reaction is the trap: an event that forcibly diverts the CPU from whatever it was doing into a designated handler, switching from user privilege to kernel privilege as it does so. The beauty is that one mechanism serves three different kinds of event, and beginners constantly muddle them, so let us keep them crisp.

A fault (or exception) comes from the currently running instruction going wrong or needing help: a divide by zero, a privilege violation, or a page that is valid but not yet in memory. It is synchronous — caused by the very instruction the CPU just tried, and reproducible if you run that instruction again. A trap in the narrow sense is a deliberate, software-requested entry into the kernel — the instruction whose entire purpose is "I want a kernel service," which is how a system call crosses the line (guide 3 dissects this in full). An interrupt is the odd one out: it comes from outside the CPU — a network card got a packet, the timer chip fired, a key was pressed — and it is asynchronous, arriving with no relation to whatever instruction happened to be executing. Same delivery machinery, three very different origins.

The dispatch table: how the CPU knows where to jump

When a trap fires, the CPU must instantly find the right handler — there is no time to look anything up in software, because the interrupted code is frozen mid-instruction. The answer is a table the kernel built during boot and pointed the hardware at: on x86 it is the interrupt descriptor table (IDT), on small ARM cores the interrupt vector table feeding the NVIC. Each event has a number — a vector — and the table is simply an array indexed by that number, where entry n holds the address of the handler for event n. The CPU's whole decision is: take the vector, index the table, jump there. It is the same idea as a function pointer array, except the hardware does the indexing for you, in nanoseconds, the moment the event arrives.

event arrives  -->  vector number n  -->  IDT[n] = &handler  -->  CPU jumps

  vector   meaning                       handler runs in
  ------   ---------------------------   ----------------
  0x00     divide-by-zero (fault)        kernel mode
  0x0E     page fault (fault)            kernel mode
  0x20     timer tick (interrupt)        kernel mode
  0x21     keyboard (interrupt)          kernel mode
  0x80     legacy system call (trap)     kernel mode

  on entry the CPU also: switches to the kernel stack,
  saves the interrupted rip/rsp/flags, raises privilege
One table, many vectors. Faults, interrupts, and a deliberate syscall trap all index the same array; the hardware switches stacks and privilege on the way in so the handler starts on solid kernel ground.

Notice what the hardware quietly does for you at the moment of entry, because it is the load-bearing magic. It does not just jump; it switches to a trusted kernel stack (you must never run kernel code on a user-controlled stack pointer), it saves enough of the interrupted state — the instruction pointer rip, stack pointer rsp, and flags — so the kernel can later resume the victim exactly where it was, and it raises the privilege level so the handler may touch hardware that user code cannot. When the handler finishes, a single "return from interrupt" instruction reverses all of this in one step: restore the saved rip/rsp/flags, drop privilege, and the interrupted instruction continues as if nothing happened. From the interrupted program's point of view, time simply skipped.

Top half, bottom half: doing little now, the rest later

Here is the tension that shapes all real interrupt handling. While a hardware interrupt handler runs, it does so in a strange and dangerous place called interrupt context: it interrupted some unlucky thread, it is not a thread itself, it has no process to block on, and — most importantly — other interrupts of equal or lower priority are held off while it runs. Every microsecond you spend in there is a microsecond the machine is partly deaf to new events. So the iron rule is: do as little as possible in the handler, and get out fast. But a network packet or disk completion genuinely needs real work done — copying buffers, waking the thread that was waiting. How do you reconcile "do little" with "do a lot"?

The resolution is to split the work in two — the top half and bottom half design. The top half is the actual hardware interrupt handler: it runs immediately, in interrupt context, with interrupts disabled, and it does only the urgent minimum — acknowledge the device so it stops yelling, stash the raw data somewhere safe, and schedule the rest of the work to happen soon. Then it returns, re-enabling the machine to hear new events. The bottom half is that scheduled remainder: the heavy lifting — parsing the packet, copying it up, waking a waiter — which the kernel runs a moment later in a more relaxed context where interrupts are on again and, in many flavors, it may even sleep.

A homely analogy: the top half is the receptionist who, when the courier rings, signs for the parcel in five seconds and drops it in the inbox — fast, never chatting, so the next courier is not kept waiting at the door. The bottom half is you, later, actually opening the parcel and dealing with what is inside. The receptionist must never sit down to read your mail while the doorbell keeps ringing. This single split — acknowledge now, process later — is the heartbeat of every well-behaved device driver, and you will meet it again the moment you write one.

Why this matters, and where it leads

Step back and see the unification. The timer interrupt that lets the scheduler take the CPU back from a hogging process, the page fault that the address space machinery handles by paging memory in, and the deliberate trap a program uses to ask for a file — all three enter the kernel through the same door, the dispatch table, with the same stack-switch-and-save dance. Learn that one door and an enormous amount of kernel behavior stops being mysterious. The kernel is, to a first approximation, a giant collection of trap handlers wired to a table, plus the threads they wake.

This also reframes a phrase you met early in the ladder. "The kernel runs in the background" is a gentle half-truth; more precisely, the kernel runs in response to traps. Most of the time the CPU is executing user code or idling, and the kernel only seizes control when the table is indexed. Even kernel preemption — the kernel's ability to switch tasks while itself running kernel code — is ultimately driven by the timer interrupt arriving and a handler deciding it is time for someone else to run. The trap is not one feature among many; it is the pulse the whole operating system beats to.

With the door understood, the next guide walks through the one trap you, the programmer, trigger on purpose thousands of times a second: the system call. You will see exactly what happens between calling read() in C and the kernel reading your file — the mode switch, how a number selects which service you want, and how the kernel safely copies data across the user/kernel boundary it just so carefully defended. Everything in this guide — the table, the privilege raise, the stack switch — is the stage; guide 3 is the play.