A CPU running millions of instructions per second and a keyboard producing a keystroke every few hundred milliseconds move at wildly different speeds — interrupts are the mechanism that lets the fast side stop wasting time waiting on the slow one.
3 min read
The simplest way for a CPU to know when a device (a disk, a network card, a keyboard) has something ready is to repeatedly ask: "is there data yet? is there data yet?" — called polling. This wastes enormous amounts of CPU time on a device that produces new input far slower than the CPU checks for it, spending nearly all of that time getting the same "not yet" answer.
An interrupt flips this around: the CPU proceeds with other useful work, and the device itself signals the CPU — asynchronously, via a dedicated hardware line — the moment it actually has something ready. The CPU then pauses whatever it was doing, runs a short interrupt handler to deal with the event, and resumes exactly where it left off.
Without interrupts, the CPU would have to dedicate real cycles to continuously checking every device for new input, starving actual application work. With interrupts, a CPU can spend nearly all its time on genuinely useful work and only pay attention to a device at the exact moment it has something worth reacting to — this is a large part of why a modern system can run dozens of I/O-bound programs at once without any of them meaningfully slowing the others down.
Lesson 4 mentioned the OS can force a running process off the CPU once its time slice expires, even if the process never voluntarily blocks. This is implemented with a timer interrupt — a piece of hardware programmed to fire an interrupt at a fixed interval, which the scheduler uses as its signal to check whether the currently-running process's time is up. Without this, a process that never calls a blocking system call could hold the CPU indefinitely, and the "fair scheduling" from lesson 5 wouldn't be enforceable at all.
Having the CPU personally shepherd every single byte between a device and memory would waste enormous CPU time on large transfers. DMA lets a device transfer data directly to or from memory without the CPU's continuous involvement — the CPU just sets up the transfer and is interrupted once when it's complete, instead of being interrupted (or occupied) for every individual byte. This is why reading a large file from a fast SSD doesn't peg the CPU at 100% for the entire duration of the transfer.
When application code calls a blocking read, the OS moves that thread to the Waiting state (lesson 4) until the underlying I/O interrupt signals completion — from the application's perspective, the call "just takes a while." Non-blocking and asynchronous I/O APIs exist specifically to let a single thread juggle many pending I/O operations without dedicating a whole blocked thread to each one — the same underlying interrupt mechanism, just exposed to application code differently.
Check your understanding
A quick comprehension check — not tracked, not graded, just for you.
1. Why is polling an inefficient way for a CPU to check for device input?
2. What role does a timer interrupt play in CPU scheduling?
3. What does Direct Memory Access (DMA) avoid, compared to having the CPU shepherd every byte of a transfer?
Operating Systems Fundamentals