Operating Systems

Processes, Threads, and Context Switches

Distinguish processes from threads by examining the resources they share and the execution state they keep separately.

About 90 min · Updated 2026-10-25

Information checked as of: 2026-10-26

Learning objectives

  • Explain how processes and threads differ in resource sharing, address spaces, and execution state.
  • Trace the state saved and restored during a context switch and analyze scheduling costs.

When a web server responds to many requests concurrently, the CPU need not be executing instructions for all of them at exactly the same instant. Work can overlap simply by alternating between requests waiting for I/O and requests ready to compute. Start by distinguishing a collection of resources from a flow of execution.

What you will learn

You will classify the information associated with processes and threads and distinguish runnable, running, and waiting states. You will also examine what must be saved when switching threads and why adding more threads does not automatically make a program faster.

A process supplies resources; a thread supplies execution state

A process is an execution environment with resources such as an address space and open files. Starting the same executable twice normally creates separate processes, each with its own variables. Even identical virtual-address values can refer to different physical memory.

A thread has execution state, including an instruction position, registers, and a stack. Threads in the same process normally share the heap, global variables, and open files. Each thread has its own stack for information such as its function-call history.

Separate stacks do not mean that one thread can never access another thread's stack. Threads share an address space, so passing a valid address may allow such access. Problems arise if an object is used after its lifetime ends or is modified without the required synchronization. Owning a resource is different from having hardware-enforced isolation.

fork separates the destinations of variable updates

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>

int main(void) {
    int value = 10;
    pid_t pid = fork();
    if (pid == -1) return 1;
    if (pid == 0) {
        value = 20;
        printf("child: %d\n", value);
        return 0;
    }
    waitpid(pid, 0, 0);
    printf("parent: %d\n", value);
    return 0;
}

The child prints 20 and the parent prints 10. Their variables initially contain the same values after fork, but ordinary writes change each process's own state. An implementation can use copy-on-write to defer copying physical pages while preserving this independence as observed by the program. Explicitly shared memory needs to be considered separately.

Being ready for the CPU versus actually using it

A runnable thread can progress if it is assigned a CPU. A running thread is actually executing instructions on a CPU. A waiting thread cannot progress until a condition is met, such as data arriving or a lock being released. OSes use different names and subdivisions, but this distinction helps separate CPU contention from I/O waits.

The scheduler chooses the next thread from runnable threads. Giving CPU time to a thread waiting for data does not make that data arrive. If response times are poor while CPU utilization is low, investigate what is being awaited: disk, network, locks, or other resources.

Saving enough state to resume where execution stopped

During a context switch, the system saves state such as registers and the instruction position so that the interrupted execution can later resume. It then restores the next execution context. Switching to a different address space also requires handling its associated management state. PCB and TCB are useful names in explanatory models; they do not necessarily correspond one-to-one with the structures used by a particular OS.

The cost of switching includes saving and restoring state as well as effects on CPU caches and address-translation state. Adding many threads to CPU-bound work can increase switching and contention. With I/O-heavy work, other threads may use the time spent waiting. Choose concurrency according to the workload.

Check your understanding

Question 1: Two threads in one process increment the same global variable. Does each thread have a separate copy? Normally no: they share the variable and must synchronize updates. Separate register state and stacks do not remove that sharing.

Question 2: Two threads alternate on a single core. Is that concurrent execution? Yes. Multiple tasks can overlap in progress even when only one instruction stream runs at an instant. Distinguish this concurrency from parallelism, in which multiple instruction streams execute simultaneously.

Virtual Memory, Page Tables, and the TLB explains how separate process address spaces are supported.