Alive does not mean running
By now you know that a process is a program brought to life, with its own address space and its own process control block holding everything the kernel knows about it. But here is a fact that surprises beginners: at any given instant, almost none of the processes on your machine are actually using the CPU. Open a laptop with hundreds of processes and a handful of CPU cores, and the truth is that the overwhelming majority are doing nothing but waiting. A process is like a recipe being cooked, but most recipes spend their time waiting for water to boil, not actively being stirred.
So if a process is not always running, what is it doing? It turns out the whole life of a process can be captured by a tiny vocabulary of just a few states. Knowing those states, and the rules for moving between them, is the key to understanding how one CPU can give a hundred programs the convincing illusion that each has the machine to itself.
The five states and the diagram that links them
The classic process state model uses five states. New is a process being created, before it is fully set up. Ready means it is loaded, complete, and willing to run — it just needs a CPU, and it sits waiting in the ready queue with all the others in the same situation. Running means it is, right now, executing instructions on a CPU; with one core, exactly one process can be Running at a time. Waiting (also called blocked) means it has asked for something that is not ready yet — most often the result of an I/O request like read(fd, buf, n) — and cannot make progress until that arrives. Terminated means it has finished and is being torn down.
admitted dispatch exit
NEW ---------> READY -----------> RUNNING -----------> TERMINATED
^ ^ | |
I/O complete | | preempt / | | I/O or wait request
or event | | quantum end | |
| +---------------+ |
| (back to READY) v
+--------------------WAITINGThe arrows matter more than the boxes. A process is admitted from New to Ready. From Ready it can only go to Running, and only the dispatcher can send it there. From Running, there are three ways out: it can be preempted back to Ready (its turn was cut short), it can block down to Waiting (it asked for I/O), or it can exit to Terminated (it is done). And here is the arrow beginners miss: when a Waiting process finally gets its I/O, it does NOT jump straight back to Running. It returns to Ready and must queue up again. The CPU is busy with someone else; it has to wait its turn like everyone else.
Sharing one CPU: a round-robin trace
Let us watch the states come alive with the simplest fair scheduler: round-robin. Each Running process is given a fixed slice of time — a time quantum, say 4 milliseconds. When the slice runs out, a timer interrupt fires, the kernel preempts the process, and the next one in the ready queue gets its turn. Suppose three processes A, B, C are all Ready and each just wants to compute for 10 ms straight. Trace it: A runs 4 ms (now needs 6 more, goes to back of queue), B runs 4 ms (needs 6 more, to the back), C runs 4 ms (needs 6 more, to the back), then A runs 4 ms again (needs 2 more), B runs 4 ms (needs 2), C runs 4 ms (needs 2), then A runs its last 2 ms and finishes, B finishes, C finishes.
Notice what each process experienced. At every quantum boundary it went Running → Ready → (wait its turn) → Running again, over and over. From the outside, all three appeared to make progress together — yet at no single instant were two of them actually executing. This is the crucial distinction: concurrency, not parallelism. Concurrency is interleaving — many tasks in flight, taking turns, possibly on just one core. Parallelism is literally-at-the-same-instant execution, and that genuinely requires multiple cores. Our trace was pure concurrency: one CPU, three processes, rapid turn-taking creating the illusion of simultaneity.
The context switch: how the CPU changes hands
Every one of those turn-takings hides a precise piece of machinery: the context switch. A CPU has only one set of registers — one program counter, one stack pointer, one set of general registers. Process A was using all of them. To let process B run, the kernel must first photograph A's complete register state and tuck it safely away, then load B's previously-saved state in its place. The register snapshot lives in each process's PCB. Think of it like a shared workbench: before the next worker steps up, you sweep the current worker's tools into their labelled drawer, then lay out the next worker's tools exactly where they left them.
- A trigger arrives — usually a timer interrupt (quantum expired) or a system call that blocks. The CPU switches into kernel mode and jumps to the kernel's handler.
- The kernel saves the current process A's full register state (program counter, stack pointer, general registers) into A's PCB, and updates A's state in the PCB (to Ready or Waiting).
- The scheduler picks the next process B from the ready queue; the dispatcher is told to switch to it.
- The kernel loads B's saved register state from B's PCB, switches the memory mappings to B's address space, and marks B as Running.
- Control returns to user mode at exactly the instruction B was about to run last time. B never notices it was paused — it simply continues.
There is one more subtlety in step 4 that costs real time. Switching address spaces can mean the cached address translations from the old process are no longer valid, so the TLB — the kernel's sticky notes for recently used page lookups — may have to be flushed. After the switch, the new process suffers a burst of TLB misses while those sticky notes are written afresh, and its working set must be re-warmed in the CPU caches. The visible switch is quick; this invisible aftermath is often the larger cost.
Switches cost, so the OS hates to make them
Here is the honest, important caveat: a context switch is pure overhead. During those microseconds of saving and restoring state, NO user process is getting useful work done — the CPU is busy being a stagehand, not an actor. The total time-from-trigger-to-the-new-process-actually-running is called dispatch latency, and the OS works hard to keep it small. This is the real reason the round-robin time quantum is a balancing act: too long and the system feels unresponsive (you wait ages for your turn); too short and you spend a painful fraction of every second just switching, with little real work in between.
It also explains a design tension you will meet again with threads: switching between two threads of the SAME process is cheaper than switching between two different processes, because the threads share one address space — no need to swap memory mappings or flush as much of the TLB. The state vocabulary you just learned applies to threads too, not just whole processes; in modern systems it is usually threads, not processes, that the scheduler shuffles between Ready, Running, and Waiting. The process is increasingly the container for resources, while the thread is the unit that actually runs.