Keyboards, screens, disks and network cards are far slower than the processor and run on their own time. Each device has a controller with a few registers: a data register, a status register ("ready?", "error?") and a command register.
The processor reaches those registers in one of two ways: special I/O instructions (IN and OUT on x86), or memory-mapped I/O, where the device registers have ordinary memory addresses and normal loads and stores reach them (used by ARM and RISC-V).
Three ways to wait for a device
Polling. The processor reads the status register again and again until the device is ready. Simple, but every check is wasted work if the device is slow.
Interrupts. The processor gets on with other work. When the device is ready it raises an interrupt signal, and the processor:
- finishes the current instruction;
- saves the PC and its state (flags, some registers);
- looks up the interrupt vector table to find the handler for this device, and runs it;
- restores the state and returns to where it was.
DMA (direct memory access). For big transfers (a disk block, a network packet), even one interrupt per byte is too many. A DMA controller copies the whole block between the device and memory by itself and interrupts the processor once, at the end.
device_ready_at = [30, 75, 120] # when a slow device has data, in microseconds
check_cost, handler_cost = 1, 3 # microseconds per status check, per interrupt
wasted = 0
t = 0
for ready in device_ready_at: # polling: keep checking until ready
while t < ready:
t += check_cost
wasted += check_cost
print("polling: CPU time spent checking:", wasted, "microseconds")
print("interrupts: CPU time spent:", handler_cost * len(device_ready_at), "microseconds")def timer():
return "switch to the next program"
def keyboard():
return "read the key code"
vectors = {0: timer, 1: keyboard} # interrupt number -> handler
pc = 1200
for number in [1, 0]:
saved = pc # save the state
print("interrupt", number, "at PC", pc, "->", vectors[number]())
pc = saved # restore it and carry onInterrupts have priorities: a timer or power failure outranks a keyboard. The timer interrupt is what lets an operating system switch between programs (see the Operating Systems course).
| Method | The CPU's work per transfer | Best for |
|---|---|---|
| Polling | constant checking | very fast devices, or very simple systems |
| Interrupts | one handler call per event | keyboards, mice, small transfers |
| DMA | one interrupt per block | disks, network cards, graphics |
Common mistakes
- Thinking an interrupt stops the current instruction in the middle: the processor finishes it first.
- Forgetting to save the state before running the handler: the interrupted program would then continue with wrong values.
- Thinking DMA means no interrupt at all: there is still one, when the transfer ends.
Quick check
My notes
Write down what you want to remember from this lesson. Notes are saved on this device as you type, and appear on your dashboard.