An interrupt is a signal that tells a processor to pause what it’s currently doing and handle something more urgent. When you press a key on your keyboard, move your mouse, or when a file finishes loading from a drive, the hardware sends an interrupt to the CPU. The processor stops its current task, deals with the new event, then picks up exactly where it left off. This mechanism is fundamental to how every modern computer operates.
How Interrupts Work
Think of an interrupt like a tap on the shoulder. Your computer’s processor is always busy executing instructions for whatever program is running. Without interrupts, it would have no way of knowing when something important happens elsewhere in the system, like new data arriving from a network card or a button being pressed.
The process follows a consistent sequence. First, a hardware device or software event generates a signal called a trigger. This sets a flag that tells the processor an interrupt is waiting. The processor finishes the instruction it’s currently executing, then automatically saves its current state: the exact point in the program it was running, the contents of its working registers, and other critical information. It then jumps to a small, dedicated piece of code called an interrupt service routine (ISR) that handles the event. Once the ISR finishes, the processor restores all that saved state and resumes the original program as if nothing happened.
This save-and-restore cycle is what makes interrupts invisible to the software that gets interrupted. The original program never knows it was paused. Its register values, its place in the code, everything comes back exactly as it was.
Hardware vs. Software Interrupts
There are two broad categories: hardware interrupts and software interrupts. They serve different purposes and behave differently.
Hardware interrupts come from physical devices outside the processor. Keystrokes, mouse movements, a disk drive signaling that requested data is ready, a timer chip firing at regular intervals: these are all hardware interrupts. They’re asynchronous, meaning they can arrive at any time regardless of what the processor is doing. The processor has no way to predict when you’ll press a key, so it needs a mechanism that can break in at any moment.
Software interrupts are generated by programs themselves. When an application needs something from the operating system, like reading a file or sending data over a network, it triggers a software interrupt to request that service. These are synchronous: they happen at a specific, predictable point in the code. All system calls (the way programs ask the operating system for help) are examples of software interrupts. Page fault exceptions, where a program tries to access memory that isn’t currently loaded, also fall into this category.
Priority and Masking
Not all interrupts are equally urgent. A processor might receive multiple interrupt signals at the same time, or a new interrupt might arrive while it’s already handling one. To manage this, processors assign priority levels to different interrupt sources. Higher-priority interrupts can preempt lower-priority ones, meaning the processor will pause handling a less urgent interrupt to deal with a critical one first.
Masking is the ability to temporarily block certain interrupts. When the processor is in the middle of something that absolutely cannot be interrupted (like updating a shared data structure), it can “mask” lower-priority interrupts, telling the hardware to hold those signals until it’s ready. The interrupts aren’t lost; they wait in a queue and get handled once the mask is lifted.
Some interrupts are so critical they cannot be masked at all. These non-maskable interrupts (NMIs) have the highest priority in the system and are reserved for catastrophic events: imminent power loss, memory errors, or serious hardware failures. When a power supply starts failing, the system needs to respond immediately regardless of what else is happening, so the NMI bypasses all masking.
Interrupt Latency
Interrupt latency is the delay between when an interrupt signal arrives and when the processor starts executing the handler code for it. Lower latency means faster response to events, which matters for time-sensitive applications like audio processing, robotics, or real-time control systems.
Several factors affect this delay. On the hardware side, CPU architecture plays a major role. ARM Cortex-M processors, commonly used in embedded systems, can achieve interrupt latency as low as 12 clock cycles under ideal conditions. That number climbs when the processor is in the middle of a multi-step instruction that must complete before the interrupt can be acknowledged, or when memory access is slow. On the software side, the biggest contributor to latency is time spent with interrupts disabled. Any code that masks interrupts adds directly to the worst-case response time. Keeping those masked periods as short as possible is a core principle of responsive system design.
Interrupt service routines themselves are kept deliberately short. They do the minimum work needed to acknowledge the event and capture any time-sensitive data, then hand off longer processing to the main system. This keeps the processor available to respond to the next interrupt quickly.
Interrupts vs. Polling
The alternative to interrupts is polling: having the processor repeatedly check a device to see if it has new data. Imagine checking your mailbox every 30 seconds versus having the postal carrier ring your doorbell. Polling wastes processor time on all those unnecessary checks when nothing has arrived. Interrupts let the processor do useful work until it’s actually needed.
That said, polling isn’t always worse. Research comparing the two approaches with very fast storage devices found that polling can actually complete individual operations faster. In one test, a 4KB read finished in 4.4 microseconds with polling versus 7.6 microseconds with interrupts. The interrupt approach carries overhead from generating the signal, switching context, and potentially polluting the processor’s cache. For extremely fast devices where the wait time is tiny, the overhead of setting up and handling an interrupt can exceed the time the processor would have spent just waiting.
For most situations, though, interrupts win. They free the processor to run other programs during the wait, and they scale well when many devices need attention. Polling only makes sense when response times are extremely short and predictable.
Interrupt Controllers in Modern Systems
Early PCs used a simple chip called the Programmable Interrupt Controller (PIC), specifically the Intel 8259. It supported only 8 interrupt lines. Even with two of them chained together, the system topped out at 15 available interrupt lines, which quickly became limiting as computers gained more devices.
Modern systems use the Advanced Programmable Interrupt Controller (APIC), first introduced with the Intel Pentium processor. The APIC has two parts: a local APIC built into each processor core, and one or more I/O APICs that collect interrupt signals from devices. In multi-core processors, each core (and each hardware thread in systems with hyper-threading) has its own local APIC. This is essential for distributing interrupt handling across multiple cores rather than funneling everything through a single processor.
The local APIC also handles communication between cores. When one core needs another core to do something, like pick up a new task, it sends an interprocessor interrupt through the APIC system. Modern devices communicate with the APIC using message signal interrupts (MSIs), which send interrupt information as data over the system bus rather than through dedicated physical wires. The older PIC still exists on modern platforms for backward compatibility, but it’s considered legacy at this point.
What Happens Inside the Operating System
When an interrupt fires while a user program is running, the operating system has to manage a careful transition. The CPU automatically switches from the program’s low-privilege mode to the kernel’s high-privilege mode. It saves five critical pieces of state onto the kernel’s stack: the instruction pointer (where the program was in its code), the code segment, the processor flags, the stack segment, and the stack pointer. The kernel then pushes the rest of the general-purpose registers to preserve the program’s complete state.
Once the interrupt is handled, the process reverses. The kernel restores all the general-purpose registers, then executes a special return-from-interrupt instruction that pops those five critical values back and drops the CPU’s privilege level back down. The user program resumes with no idea it was ever interrupted. This same mechanism handles system calls: even though a system call is initiated by the program itself, the kernel sets up identical saved state so the return path works the same way.

