βοΈ Module 02: Process Management & IPC
Focus: Process Anatomy, PCB & TCB, Multiprogramming vs Multitasking vs Multiprocessing vs Multithreading, Context Switching,
fork()& Copy-On-Write, Zombie/Orphan processes, and Inter-Process Communication (IPC).
π Table of Contents
- Process Concept & Memory Layout
- Process Control Block (PCB)
- Process Lifecycle: 5-State & 7-State Models
- Multiprogramming vs Multitasking vs Multiprocessing vs Multithreading
- Process vs Thread: Deep Dive
- Threading Models (1:1, N:1, M:N) & Green Threads
- Context Switching Internals
- Process Creation & Lifecycle (
fork, COW,exec,wait) - Zombie and Orphan Processes
- Inter-Process Communication (IPC) Mechanisms
- Common Mistakes & Gotchas
- Practice Problems with Solutions
- High-Yield Interview Questions & Answers
1. Process Concept & Memory Layout
A Process is a program in active execution. While a program is a passive entity sitting on a disk (executable file like a.out or .exe), a process is an active entity loaded into RAM with allocated hardware resources.
High Memory (0xFFFFFFFF / 0x7FFFFFFFFFFF)
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β KERNEL SPACE β
β (Memory mapped kernel code, page tables) β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β USER STACK β
β (Local variables, function frames, return addresses) β
β β β
β βΌ (Grows downward toward lower addresses) β
β β
β β
β β² (Grows upward toward higher addresses) β
β β β
β USER HEAP β
β (Dynamic memory: malloc, new, calloc, brk/sbrk) β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β BSS SEGMENT β
β (Uninitialized global and static variables: int x;) β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β DATA SEGMENT β
β (Initialized global and static variables: int x = 5;) β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β TEXT / CODE SEGMENT β
β (Compiled read-only machine instructions) β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Low Memory (0x00000000)Memory Segments Breakdown:
- Text (Code) Segment: Read-only region containing executable binary instructions. Shared among multiple instances of the same program (e.g., two terminal windows running
bash). - Data Segment: Stores initialized global and static variables (e.g.,
static int count = 100;). - BSS Segment (Block Started by Symbol): Stores uninitialized global and static variables (e.g.,
int global_array[1000];). Takes $0$ bytes in the executable file on disk because the kernel zeros it out at runtime. - Heap Segment: Dynamically allocated memory at runtime via
malloc(),calloc(), ornew. Managed by the programmer / language runtime. - Stack Segment: Automatically manages function call frames, local variables, parameters, and return addresses. Follows LIFO (Last-In-First-Out).
2. Process Control Block (PCB)
The Process Control Block (PCB) (called struct task_struct in the Linux kernel) is the central data structure the OS kernel uses to represent and track a process.
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β PROCESS CONTROL BLOCK (PCB) β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β’ Process ID (PID) & Parent PID (PPID) β
β β’ Process State (READY, RUNNING, WAITING, ZOMBIE) β
β β’ Program Counter (PC / RIP) β
β β’ CPU Registers (RAX, RBX, RCX, RSP, RBP, EFLAGS) β
β β’ CPU Scheduling Info (Priority, CFS vruntime, nice) β
β β’ Memory Management Info (Page Table Pointer %CR3) β
β β’ I/O Status Info (Open File Descriptor Table) β
β β’ Accounting & Limits (CPU time used, Max RAM, UID) β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ3. Process Lifecycle: 5-State & 7-State Models
The Classic 5-State Process Model
βββββββββββββββββββββββββββββββββββββ
β NEW β
βββββββββββββββββββ¬ββββββββββββββββββ
β Admitted
βΌ
ββββββββββββββββββββΊ βββββββββ βββββββββββββββββββ
β β READY β β
β ββββββββ βββββββββ βββββββββ β
β β β β
I/O or β Timeout β (Preemption) Selected β by β I/O or
Event β β β Sched β Event
Done β βΌ βΌ β Wait
β βββββββββββββββββββββββββββββββββ β
β β RUNNING βββββββββ
β βββββββββββββββββ¬ββββββββββββββββ
β β Exit
β βΌ
βββββββββ βββββββββββββ
βWAITINGβ βTERMINATED β
βββββββββ βββββββββββββ- New: The process is being created (memory allocated, PCB initialized).
- Ready: The process is loaded in RAM and waiting to be assigned a CPU core by the scheduler.
- Running: The process's instructions are actively being executed on a CPU core.
- Waiting (Blocked): The process cannot run because it is waiting for an external event (disk I/O, network packet, lock acquisition, timer sleep).
- Terminated: The process has finished execution; resources are released, but PCB may remain until reaped by parent.
The 7-State Model (With Memory Swapping)
When physical RAM runs low, the OS moves inactive processes from RAM to the swap space on disk:
- Ready Suspended: Process is ready to execute, but its address space has been swapped to disk.
- Blocked Suspended: Process is waiting for an I/O event AND has been swapped to disk.
4. Multiprogramming vs Multitasking vs Multiprocessing vs Multithreading
These four terms are frequently tested in interviews and often confused:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β MULTIPROGRAMMING β
β β’ Single CPU core. β
β β’ Multiple programs in RAM simultaneously. β
β β’ CPU switches ONLY when current job waits for I/O (Non-preemptive). β
β β’ Goal: Maximize CPU Utilization. β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β MULTITASKING (Time-Sharing) β
β β’ Single or Multi-core CPU. β
β β’ Time-slicing via timer interrupts (Preemptive). Rapid switching (~10-50ms). β
β β’ Goal: Minimize User Response Time (Illusion of simultaneous execution). β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β MULTIPROCESSING β
β β’ Physical Hardware feature: 2+ CPU cores or physical processors. β
β β’ True True Parallel Execution: Multiple processes execute at the EXACT same nanosecond.β
β β’ Goal: Maximize Throughput / Parallel computing power. β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β MULTITHREADING β
β β’ Multiple execution paths (threads) within the SAME process address space. β
β β’ Shared code, data, heap, and file descriptors; Private stack & registers. β
β β’ Goal: Responsiveness, resource sharing, and fine-grained concurrency. β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββComparison Matrix
| Feature | Multiprogramming | Multitasking | Multiprocessing | Multithreading |
|---|---|---|---|---|
| Core Goal | High CPU utilization | Low response time | High throughput | Fine-grained parallelism |
| Preemption? | No (switches on I/O) | Yes (time quantum) | Yes | Yes |
| Hardware Needed | 1 CPU is enough | 1 CPU is enough | 2+ Physical CPU cores | 1+ CPU cores |
| Address Space | Separate per process | Separate per process | Separate per process | Shared within process |
5. Process vs Thread: Deep Dive
A Thread is the smallest unit of execution that the OS scheduler can dispatch. It is often called a Lightweight Process (LWP).
SINGLE-THREADED PROCESS MULTI-THREADED PROCESS
ββββββββββββββββββββββββββββββββββββββββ ββββββββββββββββββββββββββββββββββββββββ
β Code β Data β BSS β Heap β β Code β Data β BSS β Heap β
ββββββββββββββββββββββββββββββββββββββββ€ ββββββββββββββββββββββββββββββββββββββββ€
β Open File Descriptors β β Open File Descriptors β
ββββββββββββββββββββββββββββββββββββββββ€ ββββββββββββββββ¬βββββββββββββββ¬βββββββββ€
β Registers β Program Counter (PC) β β Registers T1 β Registers T2 β Regs T3β
ββββββββββββββββββββββββββββββββββββββββ€ ββββββββββββββββΌβββββββββββββββΌβββββββββ€
β Stack (T1) β β Stack T1 β Stack T2 βStack T3β
ββββββββββββββββββββββββββββββββββββββββ ββββββββββββββββ΄βββββββββββββββ΄βββββββββWhat is Shared vs Private?
| Shared Between Threads of Same Process | Private to Each Individual Thread |
|---|---|
| β’ Text / Code Segment (Instructions) | β’ Thread ID (TID) |
| β’ Data & BSS Segment (Global / Static vars) | β’ Program Counter (PC / RIP) |
β’ Heap Segment (malloc / dynamically allocated memory) | β’ CPU Register Set (RAX, RBX, etc.) |
| β’ Open File Descriptors (Sockets, files) | β’ Individual Call Stack & Stack Pointer (%RSP) |
| β’ Process ID (PID) and User/Group permissions | β’ Thread-Local Storage (TLS) |
| β’ Signal Handlers | β’ Signal Mask & Errno variable |
Why Threads are "Lighter" than Processes:
- Creation Overhead: Spawning a thread doesn't require duplicating page tables or address spaces.
- Context Switch Speed: Switching between threads of the same process keeps the same page table (
%CR3), meaning the TLB (Translation Lookaside Buffer) is NOT flushed and CPU caches remain warm. - Communication: Threads share memory directly (no syscalls needed for IPC), whereas processes require pipes, sockets, or shared memory syscalls.
6. Threading Models (1:1, N:1, M:N) & Green Threads
1:1 (Kernel-Level Threads) N:1 (User-Level Threads) M:N (Hybrid / Goroutines)
ββββββ ββββββ ββββββ ββββββ ββββββ ββββββ ββββββ ββββββ ββββββ
β T1 β β T2 β β T3 β β T1 β β T2 β β T3 β β G1 β β G2 β β G3 β User
βββ¬βββ βββ¬βββ βββ¬βββ βββββ¬β΄βββββ¬βββββ΄βββββ¬βββ βββββ¬β΄βββββ¬βββββ΄βββββ¬βββ
β β β β User Thread Runtime β Go / Loom Runtime
βΌ βΌ βΌ βΌ βΌ βΌ
ββββββ ββββββ ββββββ ββββββ βββββββ βββββββ
β K1 β β K2 β β K3 β Kernel β K1 β Kernel β K1 β β K2 β Kernel
ββββββ ββββββ ββββββ ββββββ βββββββ βββββββ
(Linux pthreads, Java threads) (Early Java, Python Greenlets) (Go Goroutines, Erlang)- 1:1 Model (Kernel-Level Threads - NPTL on Linux):
- Every user thread maps directly to an OS kernel thread.
- Advantage: True multicore parallelism. If Thread 1 blocks on disk I/O, Thread 2 runs unaffected on another core.
- Cost: Thread creation and context switches require kernel traps.
- N:1 Model (User-Level Threads / Green Threads):
- Multiple user threads mapped to a single kernel thread via a user-space library.
- Advantage: Ultra-fast creation and switching (no syscalls).
- Fatal Flaw: If ONE user thread issues a blocking I/O syscall, the entire process blocks. Cannot use multiple CPU cores.
- M:N Model (Two-Tier / Go Goroutines / Java Project Loom):
- Multiplexes $M$ lightweight user routines onto $N$ kernel OS threads.
- Combines instant context switches with multicore scalability and non-blocking I/O multiplexers (
epoll).
7. Context Switching Internals
A Context Switch is the procedure by which the CPU saves the state of the currently executing process/thread and restores the state of another ready process/thread.
PROCESS P0 OPERATING SYSTEM PROCESS P1
ββββββββββββββββββββ βββββββββββββββββββββββββ ββββββββββββββββββββ
β Executing code β β β β β
β ββββ[ Interrupt / Trap ]βΊβ Saves P0 State to β β β
β β β PCB0 (Regs, PC, Stack)β β β
β β βββββββββββββββββββββββββ€ β β
β β β Scheduler picks P1 β β β
β β βββββββββββββββββββββββββ€ β β
β β β Loads P1 State from β β β
β β β PCB1 (Regs, PC, %CR3) ββββ[ `sysret` ]ββββΊβ Executing code β
β β βββββββββββββββββββββββββ β βStep-by-Step Breakdown of a Process Context Switch:
- Interrupt Trigger: Hardware timer expires (preemption) or process makes a blocking I/O system call.
- Save CPU Context: Hardware & OS push all general-purpose registers (
RAX,RBX,RSI,RSP,RIP,EFLAGS) onto the process's Kernel Stack. - Update PCB: State of $P_0$ is changed from
RUNNINGtoREADYorWAITING. - Run Scheduler: The CPU scheduling algorithm selects next candidate process ($P_1$).
- Switch Address Space: The OS updates the CPU's
%CR3register to point to $P_1$'s Page Table base. This causes a TLB Flush. - Restore CPU Context: Load registers from $P_1$'s PCB / Kernel Stack into hardware registers.
- Resume Execution: Execute return instruction (
sysret/iret), setting the Program Counter to $P_1$'s saved%RIP.
The Hidden Cost: Direct vs Indirect Overhead
- Direct Overhead: ~1β5 microseconds spent executing kernel context switch instructions and updating memory pointers.
- Indirect Overhead (Cache Pollution): When $P_1$ begins running, CPU L1/L2/L3 caches and the TLB are filled with data from $P_0$. $P_1$ experiences a barrage of cache misses and TLB misses for several milliseconds until its working set is warm.
8. Process Creation & Lifecycle (fork, COW, exec, wait)
1. The fork() System Call
In Unix-like systems, new processes are created by cloning an existing process using fork():
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main() {
pid_t pid = fork();
if (pid < 0) {
perror("Fork failed");
} else if (pid == 0) {
// Child Process
printf("Child: PID = %d, Parent PID = %d\n", getpid(), getppid());
} else {
// Parent Process
printf("Parent: PID = %d, Created Child PID = %d\n", getpid(), pid);
}
return 0;
}2. Copy-On-Write (COW)
Historically, fork() duplicated the entire physical RAM of the parent process, which was devastatingly slow. Modern OSes use Copy-On-Write (COW):
PARENT & CHILD AFTER FORK() (Read-Only Shared Pages)
βββββββββββββββββββββββββββββββββ
β Parent Page Table βββββ
βββββββββββββββββββββββββββββββββ β ββββββββββββββββββββββββββ
βββββΊ β Physical RAM Page 100 β (Marked READ-ONLY)
βββββββββββββββββββββββββββββββββ β ββββββββββββββββββββββββββ
β Child Page Table βββββ
βββββββββββββββββββββββββββββββββ
WHEN CHILD TRIES TO WRITE TO PAGE 100:
1. MMU detects Write to Read-Only Page -> Triggers Page Fault (Trap 14)
2. Kernel allocates NEW Physical Page 200, copies Page 100 contents into it
3. Updates Child Page Table to point to Page 200 with READ-WRITE permissions
4. Resumes child execution transparently3. exec() Family
Replaces the current process's text, data, heap, and stack segments with a completely new binary from disk (e.g., execvp("ls", args)). The PID does NOT change.
4. wait() / waitpid()
Suspends the parent process until one of its children terminates, collecting the child's exit status code and allowing the kernel to free the child's PCB.
9. Zombie and Orphan Processes
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ZOMBIE PROCESS β
β β’ A child process that has finished execution (`exit()`), but its parent has NOT yet β
β called `wait()` to read its exit status. β
β β’ State: `Z` (Zombie / Defunct). β
β β’ Memory: Releases text/data/heap/stack, but KEEPS its entry in the Kernel PCB table. β
β β’ Danger: If a server leaks millions of zombies, it exhausts the system PID pool β
β (`/proc/sys/kernel/pid_max`), preventing any new processes from spawning! β
β β’ How to Kill: You CANNOT kill a zombie with `kill -9` (it is already dead). β
β You must kill its parent or make the parent call `wait()`. β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β ORPHAN PROCESS β
β β’ A child process whose parent terminated or crashed before the child finished. β
β β’ What happens: The OS Kernel automatically reparents the orphan to **PID 1** β
β (`systemd` or `init`). β
β β’ PID 1 periodically calls `wait()` on all its adopted children, cleaning up their β
β PCBs automatically when they exit. β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββReaping Zombies in C (The SIGCHLD Signal Handler):
#include <signal.h>
#include <sys/wait.h>
void sigchld_handler(int s) {
// Non-blocking wait for all dead children
while (waitpid(-1, NULL, WNOHANG) > 0);
}
int main() {
struct sigaction sa;
sa.sa_handler = sigchld_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
sigaction(SIGCHLD, &sa, NULL);
// Now parent can fork freely without creating zombies!
}10. Inter-Process Communication (IPC) Mechanisms
Because processes run in strictly isolated address spaces, they cannot directly read or write each other's memory. The OS provides IPC mechanisms:
IPC TAXONOMY
β
βββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββ
βΌ βΌ βΌ
DATA TRANSFER SHARED MEMORY SYNCHRONIZATION
β’ Anonymous Pipes β’ POSIX Shared Memory β’ Semaphores
β’ Named Pipes (FIFOs) (`shm_open`, `mmap`) β’ File Locks (`flock`)
β’ Message Queues β’ System V `shmget` β’ Mutexes / Futexes
β’ Sockets (TCP/Unix Domain)Comparison of IPC Mechanisms
| IPC Mechanism | Scope | Speed | Data Format | Kernel Involvement per I/O |
|---|---|---|---|---|
| Anonymous Pipe | Related processes (Parent-Child) | Fast | Byte stream (Unidirectional) | Yes (Syscalls read/write) |
| Named Pipe (FIFO) | Any processes on same host | Fast | Byte stream (Half/Full duplex) | Yes (Syscalls read/write) |
| Message Queue | Any processes on same host | Moderate | Structured discrete messages | Yes (msgsnd/msgrcv) |
| Shared Memory | Any processes on same host | Blazing Fast | Raw memory buffer | None (Direct RAM access after setup) |
| Unix Domain Socket | Processes on same host | Fast | Stream or Datagram | Yes (Syscalls via socket API) |
| Network Sockets | Across different machines | Network-bound | Stream (TCP) / Packets (UDP) | Yes (Full network stack) |
1. Anonymous Pipe Code Example
int fd[2]; // fd[0] = Read end, fd[1] = Write end
pipe(fd);
if (fork() == 0) {
close(fd[0]); // Child closes unused read end
char msg[] = "Hello Parent!";
write(fd[1], msg, sizeof(msg));
close(fd[1]);
} else {
close(fd[1]); // Parent closes unused write end
char buf[128];
read(fd[0], buf, sizeof(buf));
printf("Parent received: %s\n", buf);
close(fd[0]);
}11. Common Mistakes & Gotchas
[!CAUTION]
1. Fork Bombs and Uncontrolled Recursion
Calling
fork()in a loop without proper termination (while(1) fork();) will exponentially spawn $2^N$ processes, instantly crashing the system by exhausting the process table and RAM.
[!WARNING]
2. Forgetting to Close Pipe Ends
If the parent process does not close its unused write-end of a pipe (
close(fd[1])), a child reading fromfd[0]will hang forever, becauseread()only returnsEOF(0 bytes) when ALL write descriptors across the entire system are closed!
[!NOTE]
3. Confusing Thread Stacks with Heap
Thread stacks are isolated (each thread has its own
%RSP), but because threads share a single address space, a thread can technically write to another thread's stack if it obtains a raw pointer to it. This leads to silent memory corruption bugs.
12. Practice Problems with Solutions
Problem 1: fork() Execution Counting
Question: How many times will the string "Hello" be printed by the following code?
#include <stdio.h>
#include <unistd.h>
int main() {
fork();
fork();
fork();
printf("Hello\n");
return 0;
}Solution:
- Initial state: $1$ process ($P_0$).
- 1st
fork(): Creates $1$ child $\rightarrow$ Total = $2$ processes. - 2nd
fork(): Both processes fork $\rightarrow$ Total = $4$ processes. - 3rd
fork(): All $4$ processes fork $\rightarrow$ Total = $8$ processes ($2^3$). - Each of the $8$ concurrent processes reaches
printf("Hello\n"). - Answer:
"Hello"is printed 8 times. - General Formula: For $n$ consecutive
fork()calls, total processes created $= 2^n$. Number of child processes $= 2^n - 1$.
Problem 2: Process State Identification
Question: Trace the process state transitions for the following scenario:
- Process is launched from terminal.
- Process calls
read(fd, buffer, 1024)from a hard drive file. - Disk controller completes data read into RAM buffer.
- Process time slice expires during loop computation.
- Process finishes execution.
Solution:
- $\text{NEW} \rightarrow \text{READY}$ (Admitted to memory).
- $\text{READY} \rightarrow \text{RUNNING} \rightarrow \text{WAITING / BLOCKED}$ (Voluntarily yields CPU waiting for disk I/O).
- $\text{WAITING} \rightarrow \text{READY}$ (Disk interrupt fires, ISR marks process ready in scheduler queue).
- $\text{READY} \rightarrow \text{RUNNING} \rightarrow \text{READY}$ (Preempted by timer interrupt; moved to ready queue).
- $\text{RUNNING} \rightarrow \text{TERMINATED}$ (Calls
exit()).
13. High-Yield Interview Questions & Answers
Q1: What happens during a fork() call under Copy-On-Write (COW)?
Answer:
- The kernel allocates a new PCB (
task_struct) and assigns a unique PID to the child. - The kernel copies the parent's page table entries to the child rather than duplicating physical memory pages.
- Both parent and child page table entries are marked as Read-Only.
- If either process attempts to modify a page, a hardware page fault exception is raised.
- The kernel handles the page fault by allocating a new physical page frame, copying the original data, and mapping the newly allocated page with write permissions to the modifying process.
Q2: What is the difference between a Zombie process and an Orphan process? Which one is dangerous and why?
Answer:
- A Zombie has completed execution, but its parent has not yet invoked
wait(). Its memory is freed, but its PCB slot and PID remain allocated in the kernel. - An Orphan is still executing, but its original parent has died. It is automatically adopted by PID 1 (
systemd/init). - Danger: Zombies are dangerous if leaked in large quantities because the OS has a finite PID capacity (
pid_max). If the PID table is exhausted, the OS cannot spawn any new processes, halting system daemons and new incoming user requests.
Q3: Why is Context Switching between Threads faster than between Processes?
Answer:
- No Memory Space Switch: Threads of the same process share the same virtual address space. The CPU does not need to reload the page table base register (
%CR3). - TLB Preservation: Because
%CR3is not changed, the Translation Lookaside Buffer (TLB) is not flushed, preserving cached virtual-to-physical address translations. - CPU Cache Warmth: L1/L2 data and instruction caches remain valid for the next thread if it accesses common process heap and text segments.
Q4: What are the primary IPC mechanisms in Linux, and which is the fastest?
Answer:
The primary mechanisms are Anonymous Pipes, Named Pipes (FIFOs), Message Queues, Unix Domain Sockets, Network Sockets, and Shared Memory.
Shared Memory (shm_open/mmap) is the fastest because once the memory region is mapped into the virtual address spaces of both processes, data reads and writes occur directly via CPU memory bus instructions without crossing into kernel mode or performing buffer copies.
Q5: If a multi-threaded process calls fork(), what gets copied into the child process?
Answer:
Under standard POSIX semantics, fork() in a multi-threaded program copies the address space, but only the single thread that invoked fork() is duplicated in the child. All other threads vanish. This is extremely hazardous if another thread was holding a mutex lock at the moment of fork(), because that lock will remain permanently locked in the child process without an owner, causing immediate deadlocks! For this reason, fork() in multi-threaded programs should almost always be immediately followed by exec().
Proceed to Module 03: CPU Scheduling Algorithms!