Every program you run becomes a process: an instance of that program in execution. The OS creates processes, tracks them, and switches between them thousands of times per second.

What is a Process?#

A process is more than just code. The OS wraps each running program in a container that holds everything needed to execute it:

text

Each process gets its own isolated address space. It cannot read or write another process's memory directly. This isolation is fundamental to security and stability.

Process Control Block (PCB)#

The OS tracks each process using a Process Control Block (PCB), a data structure in kernel memory that stores:

FieldDescription
Process ID (PID)Unique identifier
StateReady / Running / Waiting / Terminated
Program CounterAddress of the next instruction
CPU RegistersSaved register values during context switches
Memory infoPage table base address, limits
Open filesFile descriptor table
Scheduling infoPriority, CPU time used

Process States#

A process moves through these states during its lifetime:

NewReadyRunningWaitingTerminatedfork()scheduler picks itexit()I/O requestI/O donepreempted
  • New: process is being created
  • Ready: loaded into memory, waiting for CPU time
  • Running: currently executing on a CPU core
  • Waiting: blocked on I/O, a lock, or a timer
  • Terminated: finished; OS reclaims resources

Context Switching#

When the OS switches from process A to process B, it must:

  1. Save A's CPU registers and program counter into A's PCB
  2. Load B's saved registers and program counter from B's PCB
  3. Switch the memory mapping to B's address space

This is a context switch, and it is pure overhead (no useful work is done). Modern OSes minimise it but can't eliminate it.

Python

Threads#

A thread is a unit of execution within a process. Multiple threads share the same process memory (the heap, globals, and open files), but each thread has its own:

  • Program counter
  • Stack
  • Register state

Creating a thread is much cheaper than a process fork: no address space copy, no full PCB.

Python

Processes vs Threads#

ProcessThread
MemoryOwn address spaceShared with siblings
CreationExpensive (fork/exec)Cheap (new stack only)
CommunicationIPC (pipes, sockets)Direct shared memory
Crash isolationA crash doesn't affect othersOne thread crash kills the process
Use whenIsolation matters (web server workers)Parallelism within one task

User Threads vs Kernel Threads#

  • User-space threads (green threads, coroutines): managed by a runtime library, not the OS. Fast context switches but can't truly parallelise across CPU cores on a single OS thread.
  • Kernel threads: the OS schedules them directly. True parallelism on multi-core systems; Python's threading module uses these (but the GIL limits Python-level parallelism).

Concurrency vs Parallelism#

Two threads are concurrent when both are in progress at once; they are parallel only when both are running at the same instant. One core can give you the first by switching between threads in time slices, but never the second: each task still waits while the other runs, so both finish near the end. Give each thread its own core and they genuinely run side by side: three threads on three cores finish in a third of the time. As Rob Pike put it, concurrency is about dealing with lots of things at once; parallelism is about doing lots of things at once.

What to Remember#

  • A process is an isolated execution container: code, memory, and OS metadata
  • The PCB is the kernel's record of a process; it's saved/restored on every context switch
  • Threads share memory within a process; they're fast to create but require synchronisation
  • Context switching has overhead; too many processes/threads hurts performance
  • Race conditions arise when multiple threads modify shared state without coordination