š§ OS 01: Process Structure, Memory Layout & Lifecycle
Targeted for: Juspay Elimination Round, Hackathon Part B & Systems Engineering Interviews
Key Focus: Process memory segments, Stack vs Heap internals, PCB structure, Process/Thread State Transitions, and Zombie/Orphan processes.
š 1. Process Memory Layout (Virtual Address Space)
When a program (executable file stored on disk) is loaded into memory by the OS loader (execve on Linux), it becomes an active Process. The OS provides every 32-bit (4GB) or 64-bit (128TB virtual user space) process with its own private Virtual Address Space.
High Memory (0xFFFFFFFF / 0x7FFFFFFFFFFF)
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Kernel Space (Protected) ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā Command Line Arguments ā
ā & Environment Variables (envp) ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā STACK ā
ā (Function frames, local variables, return addrs) ā
ā ā Grows DOWN ā
ā ā¼ ā
ā ā
ā ā² ā
ā ā Grows UP ā
ā HEAP ā
ā (Dynamic allocations: malloc, calloc, new, etc.) ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā BSS Segment (Uninitialized) ā
ā (Static & Global variables initialized to 0) ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā Data Segment (Initialized) ā
ā (Static & Global variables with explicit values) ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā Text / Code Segment (RO) ā
ā (Compiled binary machine instructions) ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Low Memory (0x00000000)Segment Breakdown
| Segment | Memory Type | Permissions | Growth Direction | Contents & Lifetime |
|---|---|---|---|---|
| Text / Code | Fixed size | Read-Only, Execute (r-x) | None | Compiled binary instructions. Shared among multiple instances of the same executable (e.g., multiple bash processes share one Text segment in physical RAM). Read-only to prevent self-modifying code vulnerabilities. |
Initialized Data (.data) | Fixed size | Read-Write (rw-) | None | Global, static, and constant variables with explicit non-zero initial values (e.g., static int counter = 10;, int global_flag = 1;). Allocated at compile time, persists for entire program lifecycle. |
Uninitialized Data (.bss) | Fixed size | Read-Write (rw-) | None | Block Started by Symbol. Global and static variables that are uninitialized or initialized to zero (e.g., static int buffer[1024];, int zero_var = 0;). In the ELF binary on disk, .bss takes 0 bytes of storage; the kernel zero-fills this region upon process creation. |
| Heap | Dynamic size | Read-Write (rw-) | Grows UP (toward higher addresses) | Runtime dynamic memory allocation (malloc, calloc, realloc in C; new in C++/Java). Managed by user-space runtime allocators (glibc ptmalloc, jemalloc, tcmalloc) via OS system calls (brk, sbrk, mmap). Prone to memory leaks and fragmentation. |
| Stack | Dynamic size | Read-Write (rw-) | Grows DOWN (toward lower addresses on x86/ARM) | Stores Stack Frames (Activation Records): function parameters, return addresses, saved frame pointers (RBP/EBP), and local (auto) variables. Allocated and deallocated automatically on function call (call / push) and return (ret / pop). Fast allocation (just shifting RSP register). |
š 2. Deep Dive: Stack vs Heap Internals
The Stack Frame (Activation Record) Anatomy
When a function foo(a, b) is invoked, the CPU pushes a stack frame onto the process stack:
Higher Memory Addresses
āāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Function Argument 2 ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā Function Argument 1 ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā Return Instruction PTR ā <-- Where to jump after function ends (RIP)
āāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā Saved Base Pointer ā <-- Previous frame's RBP
āāāāāāāāāāāāāāāāāāāāāāāāāāāā⤠<-- Current RBP points here
ā Local Variable 1 ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā Local Variable 2 ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāā⤠<-- Current RSP (Stack Pointer) points here
Lower Memory Addresses- Stack Overflow: Occurs when recursion is too deep or massive local arrays (e.g.,
int arr[10000000];) are declared on the stack, pushing RSP past the stack boundary (typically 8MB on Linux,ulimit -s) into an unmapped page or the Heap guard page, triggering aSIGSEGV(Segmentation Fault).
Dynamic Memory Allocation (Heap Management)
- Small Allocations ($< 128\text{ KB}$): Managed by expanding the
brkpointer using thesbrk()system call. The heap is a contiguous chunk of virtual memory. - Large Allocations ($\ge 128\text{ KB}$): Allocated outside the main heap using
mmap(MAP_ANONYMOUS | MAP_PRIVATE), creating an isolated memory-mapped page region. Whenfree()is called on anmmapallocation, memory is returned immediately to the OS kernel. - Heap Fragmentation:
- Internal Fragmentation: Memory allocated to a block is slightly larger than requested (e.g., allocator aligns 13 bytes to 16 bytes).
- External Fragmentation: Free blocks are scattered throughout the heap; total free memory is sufficient, but no single contiguous block is large enough for the requested allocation.
šļø 3. Process Control Block (PCB)
The PCB (in Linux kernel: struct task_struct in <linux/sched.h>) is the data structure maintained by the OS kernel for every active process. It is stored in protected kernel space.
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Process Control Block (PCB) ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā 1. Process Identifier (PID, PPID, TGID, UID, GID) ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā 2. Process State (RUNNING, INTERRUPTIBLE, STOPPED) ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā 3. Program Counter (PC / RIP) ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā 4. CPU Registers (RAX, RBX, RCX, RDX, RSP, RBP, etc.) ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā 5. CPU Scheduling Info (Priority, Nice, vruntime) ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā 6. Memory Management Info (Page Table Base / CR3, ā
ā Virtual Memory Areas - struct mm_struct) ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā 7. I/O & File Descriptor Table (struct files_struct) ā
ā FD 0: stdin, FD 1: stdout, FD 2: stderr, sockets...ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā 8. Signal Handlers & Pending Signals ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā 9. Accounting & Resource Limits (CPU time, max mem) ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā[!IMPORTANT] Linux Thread Unification: In Linux, there is no separate "Thread Control Block" entity in the kernel. Both processes and threads are represented by
struct task_struct. Threads created viaclone()share the samemm_struct(memory),files_struct(file descriptors), andsignal_struct, while having distinct PID/TID and registers.
š 4. Process State Lifecycle
A process undergoes 5 classic state transitions throughout its lifetime:
stateDiagram-v2
[*] --> NEW : fork() / execve()
NEW --> READY : Admitted by Long-term Scheduler
READY --> RUNNING : Dispatched by Short-term Scheduler
RUNNING --> READY : Time Quantum Expired (Timer Interrupt) / Preempted
RUNNING --> WAITING : I/O Request / Sleep / Wait for Lock / Event
WAITING --> READY : I/O Completed / Event Occurred
RUNNING --> TERMINATED : exit() / Killed by Signal
TERMINATED --> [*] : Parent calls wait() / waitpid()Detailed State Transitions
- NEW (Created): The process is being created (memory allocated, PCB initialized, binary loaded), but has not yet joined the executable pool.
- READY / RUNNABLE: The process resides in main memory and is placed in the OS Ready Queue. It is waiting for the CPU scheduler to allocate a core.
- RUNNING: The CPU core is currently executing instructions from this process's code segment.
- WAITING / BLOCKED / SLEEPING: The process cannot execute even if CPU cores are idle. It is waiting for an external event:
- I/O operation (disk read, network socket receive).
- Synchronization primitive (acquiring a Mutex or Semaphore).
- Explicit sleep timer (
sleep(5)). - In Linux:
TASK_INTERRUPTIBLE(woken up by signals) vsTASK_UNINTERRUPTIBLE(ignores signals, e.g., waiting for direct disk I/O; shows asDstate inps).
- TERMINATED (Zombie / Dead): Execution finished (
exit(0)), resources (memory, file descriptors) are released, but the process entry remains in the process table so its parent can read its exit status.
š 5. Special Process Types: Zombie vs Orphan vs Daemon
1. Zombie Process (<defunct>)
- Definition: A child process that has completed execution (
exit()), but its parent has not yet calledwait()orwaitpid()to read its termination exit code. - Resource Usage: Consumes zero CPU and zero RAM (its address space is fully deallocated), but retains an entry in the OS Process Table and a PID.
- Hazard: If a parent forks millions of children without calling
wait(), it exhausts the OS process table / PID limit (/proc/sys/kernel/pid_max), preventing any new processes from spawning on the server. - How to Kill a Zombie: You cannot kill a zombie with
kill -9 <PID>because it is already dead. You must:- Send
SIGCHLDto the parent to triggerwait(). - Kill the parent process (
kill -9 <Parent_PID>), which turns the zombie into an Orphan adopted byinit/systemd, which immediately reaps it.
- Send
2. Orphan Process
- Definition: A running process whose parent process terminates before the child finishes.
- Resolution: The OS automatically reparents the orphan process to PID 1 (
initorsystemd). When the child eventually finishes, PID 1 automatically callswait()to reap it. Orphans do not cause system resource leaks.
3. Daemon Process
- Definition: A background, non-interactive service process running indefinitely without an attached controlling terminal (e.g.,
sshd,nginx,mysqld,cron). - Creation Mechanism (Daemonization):
fork()from the parent and have the parentexit()(ensures daemon is not a process group leader).- Call
setsid()to create a new session and detach from controlling TTY. - Change working directory to root (
chdir("/")) to avoid locking unmounted file systems. - Reset file mode creation mask (
umask(0)). - Close standard file descriptors (FD 0, 1, 2) and redirect them to
/dev/null.
š¬ 6. Verbatim Interview Scripts (Say This Out Loud)
Q: "Explain the difference between .data and .bss segments. Why does .bss exist?"
Verbatim Answer:
"Both.dataand.bssstore global and static variables. The key distinction lies in initialization and binary size optimization.The
.datasegment contains global and static variables that are explicitly initialized with non-zero values (e.g.,int x = 42;). The compiler must write the actual bytes42into the compiled binary on disk.The
.bss(Block Started by Symbol) segment holds global and static variables that are uninitialized or initialized to zero (e.g.,int arr[100000];). If the binary stored 100,000 zeros on disk, executable files would be massive. Instead, the binary header simply records the size of the.bsssection (400 KB). When the OS loader maps the process into memory, it allocates zero-filled pages on demand. This saves disk storage and network download time for executables."
Q: "What is a Zombie Process, how do you debug it, and how do you remove it?"
Verbatim Answer:
"A zombie process is a child process that has finished execution viaexit(), but its PCB and PID remain in the kernel process table because its parent hasn't collected its exit status usingwait()orwaitpid().A zombie uses no CPU or RAM, but it leaks PID table entries. You identify them using
ps aux | grep 'Z'or looking for[defunct].You cannot terminate a zombie using
kill -9because the process is already terminated. To clear it, you either signal the parent withSIGCHLDso it executes a wait handler, or you kill the parent process. Once the parent dies, the init process (PID 1 / systemd) adopts the zombie and immediately callswait()to reap it."