Back to Hub
Operating Systems
Operating SystemsIntermediate

Processes, Threads & Concurrency

#process#thread#concurrency#parallelism#context-switch

Processes, Threads & Concurrency

Process vs Thread

A process is an independent program in execution with its own memory space. A thread is the smallest unit of execution within a process, sharing the process's memory.

AspectProcessThread
MemoryOwn address spaceShared within process
Creation costHeavy (fork, copy-on-write)Lightweight
CommunicationIPC (pipes, sockets, shared memory)Direct memory access
IsolationFull — crash doesn't affect othersShared — one crash can kill all
Context switch costHigh (TLB flush, page table swap)Low (same address space)

Process States

Context Switching

When the OS switches the CPU from one process/thread to another:

  1. Save the state (registers, PC, stack pointer) of the current process
  2. Load the state of the next process
  3. Flush/update TLB and cache (for process switch)

Cost: ~1–10 μs per switch. With thousands of context switches/second, this overhead matters.

Concurrency vs Parallelism

ConceptDescriptionExample
ConcurrencyManaging multiple tasks (interleaving)Single-core OS running multiple apps
ParallelismExecuting multiple tasks simultaneouslyMulti-core CPU, each core runs a thread

Concurrency Models

typescript
// 1. Multi-threading (shared memory) // Java, C++, Rust — true parallelism on multi-core // 2. Event loop (single-threaded concurrency) // Node.js, browser JS — non-blocking I/O async function fetchAll(urls: string[]) { // Concurrent I/O, single thread const promises = urls.map(url => fetch(url)); return Promise.all(promises); } // 3. Actor model (message passing) // Erlang/Elixir — each actor has own state, communicates via messages // No shared memory → no locks needed // 4. CSP (Communicating Sequential Processes) // Go channels — goroutines communicate via typed channels

Scheduling Algorithms

AlgorithmTypeDescriptionProsCons
FCFSNon-preemptiveFirst come, first servedSimpleConvoy effect
SJFNon-preemptiveShortest job firstOptimal avg wait timeStarvation of long jobs
SRTFPreemptiveShortest remaining timeBetter than SJFStarvation, overhead
Round RobinPreemptiveFixed time quantumFairHigh context switch overhead
PriorityBothBased on priority valueFlexiblePriority inversion
CFS (Linux)PreemptiveCompletely Fair SchedulerFair, O(log n) via red-black treeComplex

Round Robin Example

Time quantum = 4ms
Processes: P1(10ms), P2(4ms), P3(6ms)

Timeline:
[P1: 0-4] → [P2: 4-8] → [P3: 8-12] → [P1: 12-16] → [P3: 16-18] → [P1: 18-20]

P2 finishes at 8ms
P3 finishes at 18ms
P1 finishes at 20ms
Average turnaround = (20 + 8 + 18) / 3 = 15.3ms

Inter-Process Communication (IPC)

MechanismSpeedComplexityUse Case
PipesFastSimpleParent-child, unidirectional
Named Pipes (FIFO)FastSimpleUnrelated processes
Message QueuesMediumMediumStructured messages
Shared MemoryFastestComplex (needs sync)High-throughput data sharing
SocketsVariableMediumNetwork/local communication
SignalsFastSimpleAsync notifications (SIGTERM, SIGKILL)
Memory-mapped FilesFastMediumLarge data sharing, persistence

Fork & Exec (Unix Process Creation)

fork() — Creates a child process (copy of parent)
  ├── Returns 0 in child
  └── Returns child PID in parent

exec() — Replaces current process image with new program
  └── fork() + exec() = spawning a new program

Copy-on-Write (COW):
  └── Pages shared until one process writes → then copied
  └── Makes fork() efficient even for large processes

Practical Considerations

  • I/O-bound tasks → Use async/event-loop (Node.js) or thread pool
  • CPU-bound tasks → Use multi-process (worker threads, child processes)
  • In Python → GIL prevents true thread parallelism; use multiprocessing
  • In Go → Goroutines are lightweight green threads (~4KB stack)
  • In Rust → Threads with ownership model prevents data races at compile time