Processes and the Process Model

Double-click your text editor. Now double-click it again, and open a second window. You are running the same program twice — the same file, byte for byte, sitting on your disk — yet the two windows do not share a cursor, a scroll position, or the words you type. Close one; the other lives on. Something has made two independent, living things out of one lifeless file.

That living thing is a process. The file on disk is just a recipe; a process is the cooking. The whole job of an operating system is to conjure these living processes into existence, give each one a slice of the CPU and memory, and keep them from treading on one another. Understanding the difference between the recipe and the cooking — between a program and a process — is the foundation of everything an operating system does.

A program is not a process

These two words get used loosely in everyday speech, but in operating-systems terms they are completely different kinds of thing:

This is why one program can become many processes at once. Every window of your editor, every browser tab, every copy of a game server — all launched from the same program file, but each is its own process with its own private state. The program is the sheet music; each process is a separate live performance of it.

The classic beginner mistake is to say "a program and a process are the same thing" — or to assume that running a program once means there is exactly one process for it. Neither is true.

The process control block — the OS's index card on each process

If a process is a living thing, the operating system is the manager keeping a file on every one of them. That file is the Process Control Block (PCB) — one data structure per process, holding everything the OS needs to know to manage it and, crucially, to pause it and resume it later exactly where it left off.

What lives on the index card:

The magic trick the PCB makes possible is the context switch. When the OS wants to take the CPU away from process A and give it to process B, it saves A's registers and program counter into A's PCB, then loads B's registers and program counter out of B's PCB. A is frozen mid-thought; B thaws exactly where it was. Do this dozens of times a second and it looks as though every process is running at once — the illusion of multitasking on a single CPU.

It doesn't — not really. A single CPU core runs one instruction stream at a time. The operating system simply switches between processes fantastically quickly — giving each a few milliseconds before saving its PCB and swapping to the next. Because the switching is far faster than your eye, music, browser and editor all appear simultaneous. This sleight of hand is called concurrency, and the PCB is what makes it possible: without a saved snapshot to return to, a paused process could never be resumed.

The life of a process: five states

A process is not simply "running" or "not running". Over its lifetime it moves through a small set of states, and the operating system shepherds it from one to the next. There are five:

Step through the state-transition diagram below. Each transition is one of the moves the OS can make — watch how a process loops around ready → running many times before it finally finishes:

The arrow that trips people up is running → ready (a "preemption"). The process did nothing wrong — it wasn't blocked, it wasn't finished — the OS simply decided its turn was up (its time slice expired, or a higher-priority process appeared) and put it back in the queue to be run again shortly. Contrast that with running → waiting, where the process itself gives up the CPU because it can't continue until some I/O completes.

A worked walk-through: one process, start to finish

Let's follow a single process — call it a little program that reads a file and prints a word count — through every state, so the transitions become concrete:

  1. New. You launch it. The OS allocates a PCB, assigns it a PID (say 4021), and sets up its memory. It cannot run yet — it is being born.
  2. New → Ready. Setup is done. The process joins the ready queue, standing in line with every other runnable process, waiting for a turn on the CPU.
  3. Ready → Running. The scheduler picks it. Its registers and program counter are loaded from the PCB, and the CPU starts executing its instructions.
  4. Running → Waiting. It asks to read the file from disk — slow. Rather than waste the CPU spinning, the OS marks it waiting and hands the CPU to some other ready process.
  5. Waiting → Ready. The disk finishes; the data has arrived. The process can continue, so it goes back into the ready queue (note: not straight to running — it must wait its turn again).
  6. Ready → Running. Its turn comes round again; it resumes counting words.
  7. Running → Ready (preempted). Midway, its time slice runs out. The OS pauses it — saving state to the PCB — and lets someone else run. Soon it is scheduled once more.
  8. Running → Terminated. Finally it prints the count and exits. The OS reclaims its memory and tears down PCB 4021. The process is gone; the program file is untouched, ready to be run again tomorrow.

Notice the pattern: a real process spends its life bouncing between ready, running and waiting, visiting the CPU in short bursts, over and over, until its work is done.

Seeing it for yourself

This isn't abstract theory — your machine is doing it right now. On Linux or macOS, ps and top list live processes with their PIDs and states; on Windows, the Task Manager's "Details" tab shows the same. Here is a tiny model of the scheduler's core loop — the eternal cycle of picking a ready process, running it briefly, and moving it on according to what happened:

type State = "ready" | "running" | "waiting" | "terminated"; interface PCB { pid: number; state: State; pc: number; // program counter: where to resume burstLeft: number; // instructions still to run } // The scheduler gives each process a short time slice, then decides // its next state based on what happened during the slice. function afterTimeSlice(p: PCB, askedForIO: boolean, finished: boolean): State { if (finished) return "terminated"; // running -> terminated if (askedForIO) return "waiting"; // running -> waiting (blocked on I/O) return "ready"; // running -> ready (preempted) }

The whole operating system is, at heart, a loop like this running billions of times a day: choose a ready process, load its PCB, let it run, save its PCB, repeat.

Every process is born from another process — its parent — forming a family tree. On Unix-like systems a process calls fork() to create a near-identical child, which then often exec()s a different program to become something new. Trace the tree all the way up and every process descends from a single ancestor created at boot (traditionally init, PID 1). So your browser's PID isn't random — it was handed out when some parent process asked the OS for a new child.