Processes

A process is the OS's answer to "what is a running program?" — and it's more than the code: it's the code plus its own private memory, its own resources, and its own isolated view of the machine, as if it owned the computer. That isolation is what lets many programs run at once without corrupting each other, and understanding it explains a huge amount of how systems behave.

The OS’s central abstraction for a running program is the process. This post covers what a process actually is — a program in execution with its own isolated address space and resources — its lifecycle, how processes are created (fork/exec), and the isolation that keeps them from interfering. Processes are the unit the OS schedules and protects, and understanding them underpins threads, memory, and much of system behavior. It’s the abstraction over “a program is running.”

What a process is

A process is a program in execution — but crucially, it’s much more than the program’s code. A process bundles everything needed to run that program as an independent, isolated entity:

Process = code + private address space (code/data/heap/stack) + resources (open files, sockets) + execution state
   → an isolated, independent running instance of a program

The key idea: a process is an isolated container for a running program — it has its own memory and resources, separate from other processes, and behaves as if it owned the machine (thanks to the OS’s abstractions). This isolation is fundamental: process A cannot read or corrupt process B’s memory (the OS and hardware prevent it — the user/kernel protection from the last post, plus virtual memory). That’s why you can run many programs at once safely — each is a walled-off process. (This is also the foundation containers build on: a container is a process with extra isolation via namespaces — the Kubernetes containers post.)

The address space

The most important part of a process to understand is its address space — the memory the process sees, which is virtual (the next-but-one post covers virtual memory in depth, but the layout matters here). Every process has an address space laid out in regions:

High addresses
┌──────────────┐
│   Stack      │  ← function calls, local variables (grows down)
│      ↓       │
│              │
│      ↑       │
│   Heap       │  ← dynamically allocated memory (malloc/new; grows up)
├──────────────┤
│   Data       │  ← global/static variables
├──────────────┤
│   Code (text)│  ← the program instructions
└──────────────┘
Low addresses

Two things matter for engineers here. First, the stack vs heap distinction is fundamental across languages (from the Rust series’ Box — heap allocation — to why deep recursion causes a stack overflow, and why heap allocation is more expensive than stack). Second, this address space is private per process — each process has its own, isolated from others, which is the isolation that makes processes safe. When you understand a program’s memory as this structured, private address space, a lot of behavior (memory layout, allocation cost, stack overflows, memory isolation) becomes clear.

The process lifecycle

A process moves through states as the OS manages it, which is key to understanding scheduling (next post):

      created → READY ⇄ RUNNING → terminated
                  ↑        │
                  └─ BLOCKED ┘  (waiting on I/O or an event)

The important insight: a process is not running most of the time — it cycles between Ready (wants CPU), Running (has CPU), and Blocked (waiting on something, usually I/O). This is why “my program is slow” is often “my process is Blocked on I/O, not actually computing” — understanding the states explains where time goes. The OS scheduler (next post) manages the Ready↔Running transitions (which ready process runs when), and I/O drives the Blocked state (a process reading a file blocks until the read completes). This state model is the foundation for scheduling and for reasoning about concurrency and performance.

Creating processes: fork and exec

How are processes created? On Unix-like systems, via two syscalls that are worth understanding because they reveal the model:

The classic pattern is fork then exec: to run a new program, a process forks (creating a child copy of itself) and the child execs the new program (replacing itself with it). This is how a shell runs a command, how servers spawn workers, how one program launches another:

shell process → fork() → child (copy of shell) → exec("ls") → child is now running ls
   → parent (shell) waits for the child to finish

Understanding fork/exec demystifies process creation (it’s copy-then-replace, not create-from-nothing) and explains things like why child processes inherit the parent’s open files and environment, and how process hierarchies (parent/child trees) form. It’s also the mechanism underneath higher-level “run a program” APIs in every language. (Modern variants optimize the copy — e.g. copy-on-write memory so the fork is cheap until modified.)

Processes as the unit of isolation

The takeaway: a process is the OS’s abstraction for a running program — code plus a private, isolated address space (structured into code/data/heap/stack) plus resources plus execution state — cycling through Ready/Running/Blocked states as the OS manages it, and created by fork/exec (copy-then-replace). The defining property is isolation: each process is walled off from others (can’t touch their memory), which is what lets many programs run safely at once and is the foundation containers extend. For engineers, processes explain memory layout (stack/heap), where time goes (Blocked on I/O vs Running), and how programs launch each other — the concrete reality behind “my program is running.” The next post covers what happens when a process wants to do multiple things at once within its single address space: threads.

Key takeaways

Further reading

Sources & References