Interrupt Handling
The interrupt life cycle
- A device asserts an interrupt line (or writes to the APIC on modern x86).
- The interrupt controller prioritises and delivers the interrupt to a CPU core.
- The CPU finishes the current instruction, saves minimal state (PC, SP, flags), and vectors through the IDT to the kernel’s interrupt handler.
- The kernel’s top half (ISR) acknowledges the interrupt, copies urgent data from device registers, and schedules the bottom half.
- The top half returns. Interrupts are re-enabled.
- The bottom half (deferred work) runs later, outside the interrupt context, completing the bulk of the processing (e.g., copying data into a process’s buffer, waking blocked processes).
Top half vs bottom half
The top half runs in interrupt context — interrupts are (typically) disabled, and the handler cannot block or sleep. It must be short (tens of microseconds) to avoid losing subsequent interrupts or increasing interrupt latency for other devices.
The bottom half runs in a more permissive context (kernel thread, workqueue, or softirq). It can block, sleep, and take locks. This is where the bulk of the I/O processing happens (network-stack processing, file-system buffer updates, waking user processes).
Interrupt latency
Interrupt latency is the time from the device asserting the interrupt to the first instruction of the ISR. It has several components:
- Hardware propagation delay (nanoseconds).
- CPU finishing the current instruction (may be long — e.g., a REP MOVS string operation).
- Interrupts being disabled (the kernel disables interrupts briefly for critical sections).
- Higher-priority interrupts being serviced first.
Real-time systems require bounded interrupt latency, which means the kernel must never disable interrupts for long and must avoid instructions that cannot be interrupted.
Interrupt coalescing
For high-throughput devices (network cards), generating one interrupt per packet would overwhelm the CPU with interrupt overhead at line rate. Interrupt coalescing batches completions: the device waits until either a timer expires or a threshold number of packets accumulates, then raises one interrupt for the batch. This trades a small increase in per-packet latency for a large decrease in CPU overhead.
APIC and MSI
Modern x86 systems use the Advanced Programmable Interrupt Controller (APIC) instead of the legacy 8259 PIC. The APIC supports:
- More interrupt lines (typically 24+).
- Interrupt routing to specific cores (for load balancing or cache affinity).
- Inter-processor interrupts (IPIs) for TLB shootdowns and rescheduling.
PCIe devices support Message Signalled Interrupts (MSI/MSI-X), where the device writes a value to a special memory-mapped address instead of asserting a physical line. MSI-X allows each interrupt to have a distinct vector, eliminating the need for the ISR to poll device registers to determine the interrupt cause.
Summary
- Interrupts are handled in two phases: a short top half (ISR) and a deferred bottom half.
- Top halves run with interrupts disabled and cannot block.
- Interrupt latency must be bounded for real-time systems.
- Interrupt coalescing and MSI-X reduce overhead for high-throughput devices.