Hardware Interrupts

What is it: A hardware interrupt is a signal sent by a peripheral device (NIC, disk controller, keyboard, timer) to the CPU indicating that an event requires attention — data arrived, I/O completed, timer fired. The CPU suspends the current execution, runs the interrupt service routine (ISR) in kernel mode, then resumes.

Why not poll: Without interrupts, the CPU would poll devices in a loop: "is disk done? is NIC ready? is keyboard pressed?" — busy-waiting, consuming 100% CPU on checking rather than useful work. Interrupts let the CPU do other work until a device is actually ready. The efficiency gain is enormous for low-frequency events (disk I/O: ~1ms between request and completion — polling wastes ~1M CPU cycles during the wait).

Interrupt vs syscall: A syscall is software-initiated (user process → kernel via syscall instruction). An interrupt is hardware-initiated (device → CPU via an interrupt line). Both cause a privilege mode switch to the kernel, but the source differs. Interrupts are asynchronous — they can arrive at any time during execution.

I/O interrupt flow:

sequenceDiagram participant P as Process participant K as Kernel/CPU participant D as Disk controller P->>K: read() syscall K->>D: issue disk read request K->>K: schedule OTHER processes (not busy-waiting) D->>K: interrupt: data ready Note over K: ISR copies data to kernel buffer K->>P: unblock process, schedule it P->>P: resumes, reads data

Process calls read() → kernel issues disk read request → disk controller starts reading → CPU schedules other processes → disk controller sends interrupt when data is ready → CPU pauses current process → ISR copies data from disk buffer to kernel memory → waiting process is unblocked and scheduled → process resumes and reads data.

Timer interrupt: A hardware timer fires periodically (~every 1ms on Linux). The kernel's timer interrupt handler: updates system time, checks if the current process has exceeded its time quantum, and if so, preempts it by setting a flag that causes a reschedule at the next safe point. This is the mechanism behind preemptive scheduling — the OS regains CPU control via the timer interrupt.

Software interrupt vs hardware interrupt: Hardware interrupt: from a device. Software interrupt (trap): programmatically generated via int 0x80 (legacy Linux syscall) or syscall instruction. Used for system calls and exception handling (divide by zero, page fault).

Interrupt latency: Time from device signaling readiness to ISR starting execution. Linux: ~100µs worst case. PREEMPT_RT patch: ~10µs. Critical for real-time systems (audio processing, industrial control).

Real-world usage: Go's netpoller uses epoll, which itself relies on interrupts: when a NIC receives a packet, it triggers a hardware interrupt → kernel copies packet to socket receive buffer → epoll wakes up → goroutine waiting on the socket is unscheduled from the wait list and put on the run queue.

Common pitfall

A very high-throughput NIC generating an interrupt per packet can itself become the bottleneck — at extreme packet rates, the CPU spends more time servicing interrupts than doing useful work ("interrupt storm" / receive-livelock). This is a genuinely different failure mode from the "why not poll" argument above: polling is inefficient for low-frequency events, but at sufficiently high frequency, interrupt overhead itself dominates — which is why high-performance networking stacks use NAPI (hybrid: interrupt to start, then poll while packets keep arriving, back to interrupts once the queue drains) rather than pure interrupt-driven or pure polling.