Lesson 11 / 16
Interrupts
Notifying the processor of external events, saving context, interrupt types, and the foundation of preemptive multitasking.
Contents
The previous lessons treated the processor as a closed system: it fetches instructions, decodes them, executes them, and moves to the next one. A computer, however, interacts with the outside world. A key is pressed, a disk read completes, a packet arrives from the network. When these events will occur cannot be known in advance.
This lesson asks: how is a processor occupied with its own cycle notified that something has happened in the outside world?
Polling and Its Limit
The first solution that comes to mind is polling: the program asks the device’s status at regular intervals.
loop:
read device status
if data not ready, go to loop
process data
Polling has two costs. If the device stays unready for a long time, the processor spins without producing any work; each query spends a memory access and a few instructions. If, on the other hand, the query interval is widened, latency increases instead: the time between the event occurring and it being noticed grows longer.
Polling is not bad under every condition. When events arrive very frequently and at predictable intervals, polling can cost less than the fixed overhead of interrupt handling. It cannot, however, serve as a general solution.
The Interrupt Mechanism
An interrupt is a signal a device sends to the processor. When the processor receives this signal, it completes the instruction it is executing and transfers flow elsewhere on its own. The steps are:
- The instruction currently executing completes.
- The context is saved: the program counter and the necessary registers are written aside so that flow can later resume where it left off.
- Using the interrupt number, the address of the relevant handler is read from the interrupt vector table.
- This address is written to the program counter; the interrupt handler runs.
- When the handler finishes, the context is restored, and the interrupted program continues without noticing it was interrupted.
From the interrupted program’s point of view, nothing has happened: the registers are the same, the program counter is where it is expected to be. Only the elapsed time differs.
This mechanism’s cost is not zero. Saving and restoring context does a few instructions’ worth of work and can disturb cache contents. Keeping interrupt handlers short is therefore a design rule: the handler does only what is urgent, and leaves long-running work to a task that runs afterward.
Types of Interrupts
The events that interrupt flow are not all of one kind.
A hardware interrupt is asynchronous: it comes from an external device and has no relation to which instruction the program is on. The keyboard, disk controller, network interface, and timer belong to this class.
An exception, or trap, is synchronous: it is produced by the instruction currently executing. Division by zero, an invalid instruction, an unauthorized memory access, and a page fault belong to this class. Rerunning the same program with the same input produces the exception at the same point.
The distinction is practical: an asynchronous interrupt can be deferred, a synchronous exception cannot — the instruction cannot complete until the exception is handled.
A software interrupt is a trap the program produces at its own request, and it underlies operating system calls. A user program cannot directly access hardware for work that requires privilege (opening a file, creating a network socket); instead, it produces a trap, privileged code runs, and returns the result. The boundary between user mode and kernel mode is drawn at this point.
The Timer Interrupt and Preemption
The most important interrupt does not come from the outside world but from the processor’s own timer. This timer produces an interrupt at regular intervals.
The consequence is decisive: no program can hold the processor indefinitely. When the timer interrupt arrives, control passes to the operating system; the operating system saves the running program’s context, loads another program’s context, and flow continues from there. This scheme is called preemptive multitasking.
Without preemption, multitasking would depend on programs’ voluntary cooperation; a single program caught in an infinite loop would lock up the entire system. Process and thread scheduling, in the operating system concepts course, is built on this foundation.
Direct Memory Access
Interrupts make it cheap for a device to say “I’m ready”; but if moving the data is still the processor’s job, the gain stays limited. Taking a block read from disk byte by byte into registers and writing it to memory ties the processor down to the moving work.
Direct memory access hands the transfer off to a separate controller. The processor only defines the request: source, destination address, and length. The controller carries out the transfer itself; the processor executes other instructions in the meantime. When the transfer finishes, the controller produces an interrupt.
This scheme reveals the real gain of interrupts: the processor neither waits for the data nor moves it; it only starts the transfer and is notified when it finishes. The same principle continues in higher layers — asynchronous input/output interfaces repeat the “issue the request, collect the result when notified” pattern at the software level.
The cost is that shared memory now has two writers: while the controller modifies memory, the processor’s cache may hold a stale copy of that region. Maintaining consistency is the responsibility of hardware and the driver.
Masking and Priority
A new interrupt can arrive while an interrupt handler is running. Hardware manages this with two tools.
Masking: Specific interrupts can be temporarily suppressed. Short pieces of code that would produce an inconsistent state if interrupted — critical sections — run with interrupts disabled. Keeping this period long directly increases the system’s delay in responding to external events.
Priority: Interrupts are graded by importance; a high-priority interrupt can interrupt a low-priority handler. Nested interrupts mean contexts accumulate on the stack.
There is also a class of interrupts that cannot be suppressed at all: non-maskable interrupts, reserved for events such as hardware failure, for which deferral would be meaningless.
Reflection in the Program
An interrupt is a hardware-level concept, but its traces are visible in software. An operating system signal is the process-level equivalent of an interrupt: wherever flow happens to be, the registered handler runs and flow then continues from where it left off.
import signal, time interrupt_count = 0 def timer_handler(signal_num, frame): """Runs wherever flow happens to be, then returns.""" global interrupt_count interrupt_count += 1 signal.signal(signal.SIGALRM, timer_handler) signal.setitimer(signal.ITIMER_REAL, 0.05, 0.05) # a signal every 50 ms start = time.perf_counter() counter = 0 while time.perf_counter() - start < 0.5: # half a second of a busy loop counter += 1 signal.setitimer(signal.ITIMER_REAL, 0) # stop the timer print(interrupt_count) # around 9
The loop contains no line waiting for the signal; yet the handler has run roughly nine times. This is the distinguishing trait of the interrupt concept: control is transferred without the flow itself requesting it.
The limit of this analogy should also be noted. Operating system signals are not hardware interrupts; the runtime processes them at safe points. Still, the idea of flow being interrupted from outside is the same.
The same idea continues in higher layers: asynchronous input/output, event loops, and callback-based interfaces are the software-level equivalents of the “notify me when ready” principle.
Summary
- Polling queries device status at regular intervals; it either spends processor time or increases latency.
- An interrupt is a signal a device sends to the processor; the processor saves the context, runs the handler, and resumes flow from where it left off.
- Hardware interrupts are asynchronous, exceptions are synchronous; software interrupts underlie operating system calls.
- The timer interrupt ensures no program can hold the processor indefinitely, and makes preemptive multitasking possible.
- Interrupts are masked in critical sections; the longer masking lasts, the more the system’s response delay grows.
- Interrupt handlers are kept short; saving and restoring context is costly.
Next Step
This topic’s remaining question is the operation itself: what happens in hardware when an add instruction runs? The next lesson shows how arithmetic is built from logic gates, and why two’s complement representation needs no separate circuit for subtraction.
To keep your progress and take notes, Log in
My notes
Log in to take notes.