Loading learning content...
Imagine you're deeply focused on a complex task—writing code, reading a document, performing calculations. Now imagine that to check if someone has sent you a message, you must stop your work every millisecond, walk to the door, check if anyone is there, and then return to your desk. This constant checking—called polling—would devastate your productivity.
Early computers faced exactly this problem. The CPU, executing instructions at blazing speeds, needed to interact with comparatively glacial peripheral devices: disk drives, keyboards, network interfaces. Without an efficient mechanism for devices to signal when they needed attention, CPU time was wasted endlessly checking device status registers.
Hardware interrupts solved this fundamental problem. They allow external devices to tap the CPU on the shoulder—metaphorically raising their hand—to say, 'I have something important.' The CPU can then pause its current work, handle the device request, and resume exactly where it left off.
By the end of this page, you will understand the complete architecture of hardware interrupts: their sources, signaling mechanisms, hardware support structures, and how they enable efficient CPU utilization while maintaining system responsiveness. You'll gain deep insight into interrupt request lines (IRQs), programmable interrupt controllers (PICs and APICs), and the hardware-level mechanics that make modern computing possible.
To truly appreciate hardware interrupts, we must first understand the problem they solve. Modern CPUs execute billions of instructions per second—a 4 GHz processor completes approximately 4 billion clock cycles every second. Each instruction might complete in 1-5 cycles for simple operations.
Contrast this with peripheral devices:
The speed disparity is staggering. While a CPU waits for a single disk operation, it could have executed 40 million instructions. Wasting these cycles polling would be catastrophic for system performance.
| Component | Typical Latency | CPU Cycles Wasted (4 GHz) | Instructions Lost |
|---|---|---|---|
| L1 Cache Access | 1 ns | 4 cycles | ~4 instructions |
| L3 Cache Access | 10-20 ns | 40-80 cycles | ~50 instructions |
| Main Memory (RAM) | 100 ns | 400 cycles | ~200 instructions |
| SSD Read | 25-100 μs | 100K-400K cycles | ~100K instructions |
| HDD Seek + Read | 5-10 ms | 20-40 million cycles | ~30 million instructions |
| Network Round-Trip | 10-100 ms | 40-400 million cycles | ~200 million instructions |
Early systems used polling: the CPU repeatedly checked each device's status register in a loop. For n devices, this means O(n) checks per cycle. At scale, CPUs spent more time asking 'Are you ready?' than doing useful work. Interrupts eliminated this waste by inverting the model: devices notify the CPU when they need attention.
The Inversion of Control:
Hardware interrupts implement a fundamental design pattern—inversion of control. Instead of the CPU actively seeking status updates from devices, devices signal the CPU when events occur. This transforms CPU utilization from:
while (true) {
for (each device) {
check_if_device_ready(); // Wasteful polling
}
}
To an event-driven model where the CPU executes useful work until interrupted. This single architectural decision enabled the responsive, multi-tasking systems we use today.
A hardware interrupt is an electrical signal generated by a peripheral device and delivered to the CPU through dedicated hardware paths. Understanding the complete signal path is essential for grasping how interrupts work.
The Interrupt Signal Path:
Hardware interrupts originate from diverse sources, each serving a specific purpose in system operation. Understanding these sources is crucial for system programming and debugging.
Categories of Hardware Interrupt Sources:
Timer interrupts are the heartbeat of the operating system. Generated by programmable interval timers (PIT) or high-precision event timers (HPET), they fire at regular intervals (typically 100-1000 times per second).
Linux kernels typically configure 100-1000 Hz timer frequencies (CONFIG_HZ). Higher frequencies provide finer-grained scheduling but increase interrupt overhead. Tickless kernels (CONFIG_NO_HZ) omit timer interrupts during idle periods to save power.
Under heavy load, devices can generate interrupts faster than the CPU can handle them—an 'interrupt storm.' This can monopolize CPU time, starving user processes. Modern systems use interrupt coalescing, where devices batch multiple events into a single interrupt, and adaptive interrupt moderation that dynamically adjusts interrupt frequency based on load.
The Programmable Interrupt Controller (PIC) is the hardware intermediary between devices and the CPU. The venerable Intel 8259A PIC, introduced in 1976, established patterns still visible in modern systems. Understanding the PIC architecture illuminates fundamental interrupt management concepts.
The 8259A PIC Architecture:
A single 8259A manages 8 interrupt request lines (IRQ0-IRQ7). PC/AT systems cascade two PICs for 15 usable IRQs:
| IRQ | Classic Device | Purpose |
|---|---|---|
| IRQ0 | Programmable Interval Timer | System clock ticks (~18.2 Hz or programmed frequency) |
| IRQ1 | Keyboard Controller (8042) | Keyboard key press/release events |
| IRQ2 | Cascade to Slave PIC | Routes IRQ8-15 through master |
| IRQ3 | COM2/COM4 Serial Port | Serial communication |
| IRQ4 | COM1/COM3 Serial Port | Serial communication, modem |
| IRQ5 | LPT2 or Sound Card | Parallel port or audio |
| IRQ6 | Floppy Disk Controller | Floppy disk operations |
| IRQ7 | LPT1 Parallel Port | Printer port |
| IRQ8 | Real-Time Clock (RTC) | CMOS clock, periodic interrupts |
| IRQ9 | Available / ACPI | ACPI SCI on modern systems |
| IRQ10-11 | Available | PCI devices |
| IRQ12 | PS/2 Mouse | Mouse movement/clicks |
| IRQ13 | FPU / Coprocessor | Floating-point exceptions |
| IRQ14 | Primary IDE Controller | Hard disk operations |
| IRQ15 | Secondary IDE Controller | CD-ROM, secondary disk |
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556
// Classic 8259A PIC initialization for protected mode// Remaps IRQs to vectors 0x20-0x2F to avoid conflict with CPU exceptions #define PIC1_COMMAND 0x20 // Master PIC command port#define PIC1_DATA 0x21 // Master PIC data port#define PIC2_COMMAND 0xA0 // Slave PIC command port#define PIC2_DATA 0xA1 // Slave PIC data port #define ICW1_ICW4 0x01 // ICW4 needed#define ICW1_SINGLE 0x02 // Single mode (not cascaded)#define ICW1_INIT 0x10 // Initialization command#define ICW4_8086 0x01 // 8086/88 mode (vs MCS-80/85) void pic_remap(int offset1, int offset2) { unsigned char mask1, mask2; // Save existing masks mask1 = inb(PIC1_DATA); mask2 = inb(PIC2_DATA); // Start initialization sequence (cascade mode) outb(PIC1_COMMAND, ICW1_INIT | ICW1_ICW4); io_wait(); outb(PIC2_COMMAND, ICW1_INIT | ICW1_ICW4); io_wait(); // ICW2: Set vector offset (maps IRQ0-7 to offset1, IRQ8-15 to offset2) outb(PIC1_DATA, offset1); // Master PIC vector offset io_wait(); outb(PIC2_DATA, offset2); // Slave PIC vector offset io_wait(); // ICW3: Tell Master PIC about Slave PIC at IRQ2 outb(PIC1_DATA, 0x04); // IRQ2 has slave (bit 2 set) io_wait(); outb(PIC2_DATA, 0x02); // Slave cascade identity io_wait(); // ICW4: Set 8086 mode outb(PIC1_DATA, ICW4_8086); io_wait(); outb(PIC2_DATA, ICW4_8086); io_wait(); // Restore saved masks outb(PIC1_DATA, mask1); outb(PIC2_DATA, mask2);} // Send End-of-Interrupt (EOI) signal after handling interruptvoid pic_send_eoi(unsigned char irq) { if (irq >= 8) { outb(PIC2_COMMAND, 0x20); // EOI to slave for IRQ8-15 } outb(PIC1_COMMAND, 0x20); // EOI to master (always)}In real mode, the BIOS maps IRQ0-7 to vectors 0x08-0x0F. These conflict with CPU exception vectors (0x00-0x1F) in protected mode. The kernel must remap the PIC to non-conflicting vectors (commonly 0x20-0x2F) during initialization. Failure to remap causes interrupt handlers to be invoked for hardware interrupts when CPU exceptions occur.
The 8259A PIC, while foundational, cannot meet the demands of modern multiprocessor systems. The Advanced Programmable Interrupt Controller (APIC) architecture, introduced with Intel's Pentium processors, addresses these limitations.
Limitations of the 8259A PIC:
Message Signaled Interrupts (MSI/MSI-X):
MSI represents a paradigm shift in interrupt delivery. Instead of asserting a dedicated wire, devices write a specific value to a predefined memory address. This write triggers the interrupt:
Modern high-performance devices (NVMe SSDs, 10G+ network cards) rely heavily on MSI-X to achieve low-latency, high-throughput operations with per-queue interrupts.
Hardware interrupts can be signaled in two fundamentally different ways: edge-triggered and level-triggered. The distinction has profound implications for device drivers and interrupt handling.
Edge-Triggered Interrupts:
The interrupt is recognized on a signal transition—typically the rising edge (low-to-high transition) of the IRQ line.
Level-Triggered Interrupts:
The interrupt is recognized as long as the signal remains at a certain level (typically high).
| Characteristic | Edge-Triggered | Level-Triggered |
|---|---|---|
| Trigger Condition | Signal transition (rising/falling edge) | Signal level (high or low) |
| Interrupt Recognition | Single pulse per event | Continuous while asserted |
| Lost Interrupt Risk | Yes—second edge during handling is missed | No—line stays asserted |
| IRQ Sharing | Problematic—hard to determine who interrupted | Works well—all devices checked |
| Handler Requirement | Fast—just acknowledge | Must clear device condition |
| Latching | Edge latched in pending register | No latching needed |
| Typical Use | Timer, keyboard (legacy) | PCI devices, shared IRQs |
If a level-triggered interrupt handler returns without clearing the device's interrupt condition, the CPU will immediately re-enter the handler (the line is still asserted). This infinite loop—an interrupt storm—freezes the system. Handlers MUST service the device and clear its interrupt flag before sending EOI.
1234567891011121314151617181920212223242526272829303132333435
// Correct level-triggered interrupt handler patternvoid network_interrupt_handler(void) { uint32_t status = read_device_status_register(); // Check and handle all possible interrupt causes while (status & INTERRUPT_PENDING_MASK) { if (status & RX_COMPLETE_BIT) { // Process received packets process_received_packets(); // Clear the RX interrupt bit in device write_device_register(STATUS_REG, RX_COMPLETE_BIT); } if (status & TX_COMPLETE_BIT) { // Reclaim transmitted buffers reclaim_tx_buffers(); // Clear the TX interrupt bit in device write_device_register(STATUS_REG, TX_COMPLETE_BIT); } if (status & ERROR_BIT) { // Handle error condition handle_device_error(); // Clear the error bit write_device_register(STATUS_REG, ERROR_BIT); } // Re-read status to check for new events during handling status = read_device_status_register(); } // NOW safe to send EOI—device interrupt line is de-asserted pic_send_eoi(NETWORK_IRQ);}While most hardware interrupts can be temporarily disabled (masked) using the CPU's interrupt flag or device masks, certain critical events demand immediate attention regardless of system state. These are Non-Maskable Interrupts (NMI).
NMI Characteristics:
Linux can be configured to generate a crash dump on NMI (nmi_watchdog, panic_on_io_nmi). System administrators can trigger NMI remotely (IPMI) or physically (NMI button) to debug hung systems. The kernel's NMI handler captures register state and can force a kernel panic for post-mortem analysis.
1234567891011121314151617181920212223242526272829303132333435
// Simplified NMI handler structure// NMI handlers must be extremely careful—system state may be inconsistent void nmi_handler(struct interrupt_frame *frame) { // Immediately disable further NMIs (set bit 7 of port 0x70) // This prevents nested NMIs during handling outb(0x70, inb(0x70) | 0x80); // Check NMI reason register (port 0x61) uint8_t reason = inb(0x61); if (reason & 0x80) { // Memory parity error panic("NMI: Memory parity error detected at unknown address"); } if (reason & 0x40) { // I/O channel check (bus error) panic("NMI: I/O channel check - bus error"); } // Check for watchdog NMI (platform-specific) if (is_watchdog_nmi()) { // Watchdog expired - system may be stuck // Dump CPU state for analysis dump_stack_trace(frame); panic("NMI: Watchdog timer expired - system unresponsive"); } // Unknown NMI - log and continue cautiously printk(KERN_WARN "NMI: Unknown NMI received, reason=%02x\n", reason); // Re-enable NMIs outb(0x70, inb(0x70) & ~0x80);}We've explored the architecture and mechanisms of hardware interrupts—the foundation of responsive, efficient computing systems. Let's consolidate the key concepts:
What's Next:
Now that we understand hardware interrupts—externally generated signals from devices—we'll explore their counterpart: software interrupts (traps). These are internally generated by executing programs, either intentionally (system calls) or due to exceptional conditions (faults, exceptions). Together, hardware and software interrupts form the complete interrupt mechanism that operating systems depend upon.
You now understand hardware interrupts: their purpose, architecture, signal paths, and hardware support mechanisms. This knowledge is essential for understanding how operating systems interact with hardware and maintain system responsiveness. Next, we'll explore software-generated interrupts and traps.