Processes, Threads, and CPU Scheduling
An operating system turns hardware into managed abstractions. Processes provide protected execution environments; threads provide independently scheduled flows of execution inside those environments.
Process as a protected environment
A process includes a virtual address space, executable code, data, open resources, security identity, and at least one thread. Isolation prevents an ordinary memory access in one process from directly modifying another process.
Creating or switching processes involves kernel bookkeeping and address-space concerns. Inter-process communication—pipes, sockets, shared memory, or messages—makes exchange explicit.
- Isolation improves fault containment
- Virtual memory gives each process its own address view
- IPC crosses the isolation boundary deliberately
Threads share, stacks do not
Threads in one process normally share code, heap data, and open files, but each thread has its own registers, program counter, and call stack. Sharing makes communication cheap and races possible.
A context switch saves the current execution state and restores another. Excessive switching adds overhead and can disrupt caches even when no useful application work is completed.
- Shared heap requires coordination
- Per-thread stacks hold local call state
- User and kernel threads differ in who schedules them
Terms, operations, and practical uses
Execution units
- ProcessA protected address space with resources and at least one thread.
- ThreadAn independently scheduled instruction flow inside a process.
- Context switchSaving one execution state and restoring another.
- PCBKernel record containing a process's identity, state, scheduling data, and resources.
Scheduling
- ReadyAble to run but waiting for a CPU.
- RunningCurrently executing on a CPU core.
- WaitingBlocked until an event or resource becomes available.
- PreemptionThe scheduler interrupts running work so another thread can execute.
Concurrency safety
- Race conditionA result that depends on an uncontrolled ordering of shared operations.
- Critical sectionCode that must not overlap when it changes a protected invariant.
- DeadlockA cycle of waiting in which no participant can make progress.
Protect a shared counter
from threading import Lock, Thread
counter = 0
lock = Lock()
def worker():
global counter
for _ in range(3):
with lock:
counter += 1
a = Thread(target=worker)
b = Thread(target=worker)
a.start(); b.start(); a.join(); b.join()
print('counter =', counter)#include <iostream>
#include <mutex>
#include <thread>
using namespace std;
int counter = 0;
mutex counterMutex;
void worker() {
for (int i = 0; i < 3; ++i) {
lock_guard<mutex> guard(counterMutex);
++counter;
}
}
int main() {
thread a(worker), b(worker);
a.join(); b.join();
cout << counter << '\n';
}class Main {
static int counter = 0;
static synchronized void increment() { counter++; }
public static void main(String[] args) throws Exception {
Runnable worker = () -> { for (int i = 0; i < 3; i++) increment(); };
Thread a = new Thread(worker), b = new Thread(worker);
a.start(); b.start(); a.join(); b.join();
System.out.println(counter);
}
}two workers increment three times eachcounter = 6Run the example step by step
Races and synchronization
A race occurs when correctness depends on an uncontrolled ordering of concurrent operations. Locks protect critical sections; semaphores manage permits; condition variables let threads sleep until a state predicate may have changed.
Synchronization should protect an invariant, not merely a line of code. Keep critical sections small, define lock ownership, and use a consistent lock order.
- Atomicity: operation appears indivisible
- Visibility: writes become observable
- Ordering: events occur in required sequence
Scheduling and deadlock
Schedulers balance responsiveness, throughput, fairness, and deadlines. Round-robin time slicing favors interactive fairness, while priority policies risk starvation without aging or other correction.
Deadlock requires mutual exclusion, hold-and-wait, no forced release, and a circular wait. Preventing any one condition is sufficient; a global lock order directly breaks circular wait.
- Turnaround and response time measure different goals
- Preemption lets the scheduler interrupt
- Avoid nested locks or impose one global acquisition order