OS Notes Detailed
OS Notes Detailed
CONTEXT SWITCHING
Process Mechanism by which OS pauses one process and resumes another — creates illusion of concurrency. Triggered by
timer interrupt, I/O block, or explicit yield. Pure overhead — no useful work during the switch itself (~1–10 µs including
A process is a program in execution — includes code, current activity (PC, registers), stack, heap, and data. Every
TLB flush + cache warm).
process has a unique PID and its own isolated virtual address space. Managed via the PCB (Process Control Block):
PID, state, CPU registers, PC, memory maps, open files, scheduling info. Step Action
New Being created ② Save A's context Registers, PC, SP → A's PCB
Ready Waiting for CPU ③ Scheduler runs Picks next process B from ready queue
Running On CPU now ④ Load B's context Restores B's registers, PC, mem maps
Waiting Blocked on I/O/event ⑤ Switch mem maps MMU updated; TLB may flush (expensive)
Thread ★ Too-small time quanta in Round Robin → frequent context switches → overhead dominates useful work.
A thread is the smallest CPU execution unit. Threads in the same process share heap, code, and data but each has its
4. KERNEL & SYSTEM CALLS
own stack, registers, and PC. Cheaper to create/switch than processes.
★ Syscall is expensive — libraries like libc batch operations (fwrite buffers) to minimise syscall frequency.
5. VIRTUAL MEMORY & ADDRESS SPACE 7. TLB (TRANSLATION LOOKASIDE BUFFER)
Each process believes it owns a large contiguous private address space. OS + MMU translate virtual→physical Page table lookup on every memory access = extra RAM read = slow. TLB: small hardware cache inside MMU storing
transparently. Enables isolation (no cross-process reads), simplifies programming, and allows running more processes recent VA→PA mappings. Hit: ~1 cycle. Miss: page table walk = 10–100+ cycles. Typical hit rate: 95–99%.
than RAM physically holds.
TLB Hit TLB Miss
Region Direction Contents Action Use cached mapping Walk page table → load → retry
Stack Grows ↓ Local vars, return addrs, function frames. Fixed max (ulimit). Stack overflow = Cost ~1 cycle ~10–100+ cycles
crash.
Frequency 95–99% 1–5%
↕ Gap — Unmapped — segfault on access. Prevents stack/heap collision.
Context switch + TLB: entries are invalid for new process. Either flush entire TLB (costly) or use ASIDs (Address
Heap Grows ↑ Dynamic memory (malloc/new). Allocator-managed. Fragmentation possible.
Space IDs) to tag entries per process — avoids full flush.
Data (.data/.bss) Fixed Initialized globals (.data) + zero-init globals (.bss).
EAT = h×(tlb+mem) + (1−h)×(tlb+2×mem) [h=hit rate]
Text/Code Fixed Read-only program instructions. Shared among threads. With h=0.99, TLB=1ns, Mem=100ns → EAT≈102ns vs 200ns without TLB.
★ Same virtual address in two processes → different physical RAM. This IS process isolation. ★ Locality of memory access directly determines TLB hit rate → program structure affects performance.
9. SYNCHRONIZATION
★ 3 Requirements: ① Mutual Exclusion (one thread in CS) ② Progress (empty CS → waiter enters) ③ Bounded
Waiting (no starvation).
Mutex & Spinlock 11. INTER-PROCESS COMMUNICATION (IPC)
Mutex: binary lock — only holder can unlock. Spinlock: busy-waits in loop — no context switch, good for very short
critical sections (kernel, low-latency). Blocking mutex: thread sleeps if unavailable — better for longer waits; OS Processes have isolated address spaces. IPC = OS-provided channels for data exchange, synchronization, signaling.
wakes it on release. Kernel mediates all IPC for security. Choice involves speed vs complexity vs scope trade-offs.
✔ Spinlock: zero context-switch overhead ✘ Spinlock: wastes CPU cycles if long wait Mechanism Speed Scope Key Notes
✔ Blocking mutex: CPU freed while waiting ✘ Blocking mutex: context-switch overhead Signals Instant Local Async notify; SIGKILL/TERM/INT; no data transfer
Anon Pipe Fast Parent↔Chil Unidirectional byte stream; pipe()+fork()
Semaphores d
Integer counter with atomic wait(S)/P(): if S>0 decrement else block; and signal(S)/V(): increment + wake one blocked
Named Pipe Fast Any local FIFO file; unrelated processes
thread. Atomicity via hardware test-and-set or OS.
Shared Memory Fastest Local Direct RAM; needs mutex/sem for sync
Binary semaphore (0/1): acts like mutex. Counting semaphore (0…N): N concurrent users — DB connection pools,
bounded buffers, printer spoolers. Message Queue Medium Local Kernel-buffered structured msgs; decoupled
Unix Socket Very fast Local Bidirectional; used by DBs, Docker
Monitors & Classic Problems
TCP Socket Overhead Network Foundation of distributed systems
Monitor: high-level construct — all methods mutually exclusive. Condition variables: wait() releases lock + sleeps;
signal() wakes one waiter which re-acquires lock. Prevents missed-wakeup bug. RPC/gRPC Medium Network Serialised call; base of microservices
Problem Issue Solution ★ Shared memory = fastest IPC but REQUIRES explicit synchronization. Pipes are simplest but unidirectional
Producer-Consumer Don't overflow/underflow buffer 2 counting semaphores (empty,full) + 1 only.
mutex
12. VIRTUALIZATION
Readers-Writers Multiple readers OK; writer exclusive Reader count + mutex; writers exclude all
Run multiple OSes on one physical machine. Each OS (guest) runs in an isolated Virtual Machine (VM) with
Dining Philosophers 5 philosophers, 5 forks → deadlock risk Asymmetric pickup order or resource
ordering virtualised CPU, memory, disk, and network. The hypervisor (VMM) manages resource sharing.
Avoidance Banker's Algo: grant only if safe state remains Needs max-need declaration upfront Examples ESXi, Hyper-V, KVM, Xen VirtualBox, VMware Workstation
Detection Allow deadlocks; cycle-detect in resource graph; Detection overhead + disruptive recovery Use case Datacenters, production Dev, testing, desktop
kill/rollback
Containers vs VMs
Ignorance Assume rare; reboot (most desktop OSes) Simple; ok if deadlocks truly rare
VMs virtualise full hardware stack — each runs own kernel. Containers (Docker/LXC) share host kernel, isolated via
Starvation & Livelock & Priority Inversion Linux namespaces (PID, network, mount, user) and cgroups (CPU, memory, I/O limits).
Starvation: process waits indefinitely; system progresses but one thread suffers. Fix: aging (raise priority over time). ✔ VMs: strong isolation, any OS ✘ VMs: heavy (GBs), slow startup
Livelock: threads actively react to each other but no useful work — hallway analogy. Fix: random back-off. ✔ Containers: lightweight (MBs), ms startup, efficient ✘ Containers: shared kernel = kernel vuln affects all
Priority inversion: Low-priority L holds resource needed by High H; Medium M preempts L → H blocked by M. Mars
Pathfinder (1997). Fix: priority inheritance — raise L to H's priority while L holds resource. 13. LINUX DIRECTORY STRUCTURE (FHS)
★ Deadlock = no progress anywhere. Starvation = one thread suffers. Livelock = active but stuck. Single-root hierarchy — everything hangs off /. Physical devices, virtual FSes, and network mounts all appear as
directories. No drive letters.
/ Root of entire filesystem /opt Optional 3rd-party software outside package Storage Hierarchy & RAID
manager RAID (Redundant Array of Independent Disks): combines multiple disks for performance and/or redundancy.
/bin /usr/bin — Essential user binaries: ls, cp, mv, /proc Virtual FS: live kernel+process info — RAID Level Technique Capacity Fault Tolerance Use
bash /proc/cpuinfo, /proc/[PID]/
RAID 0 Striping 100% None (any disk fail = data Max performance
/sbin /usr/sbin — Admin binaries: fdisk, iptables, /sys Virtual FS (sysfs): hardware topology, driver loss)
sshd params, power mgmt
RAID 1 Mirroring 50% 1 disk failure Critical data
/boot Kernel (vmlinuz), initrd, GRUB config — don't /run Runtime data since boot: PIDs, sockets, locks.
RAID 5 Striping + parity (distributed) (N-1)/N 1 disk failure Balanced perf/redundancy
delete! Cleared on reboot.
RAID 6 Striping + 2 parity (N-2)/N 2 disk failures High durability
/dev Device files: /dev/sda (disk), /dev/null, /tmp Temp files; world-writable; cleared on reboot
/dev/urandom RAID 10 RAID 1+0: mirror then stripe 50% 1 disk per mirror pair High perf + redundancy
/etc All system config: passwd, fstab, hosts, ssh/, /usr Largest: user programs (/usr/bin), libs (/usr/lib),
File Systems
cron.d headers, docs
Organises data on storage. Key structures: superblock (FS metadata), inode (file metadata: permissions, size,
/home User home dirs: /home/alice — contains /var Variable data: logs (/var/log), cache (/var/cache),
timestamps, block pointers — not the filename), directory entries (name→inode), data blocks.
.bashrc, .ssh/ spool, DB files
Journaling: write intent to journal before changes — replay on crash prevents corruption (ext4, NTFS, APFS). Hard
/lib /usr/lib — Shared libraries (.so); kernel /root Home of root (superuser) — separate from /home
link: another dir entry → same inode (same data). Symlink: file containing path — can cross FSes, can be broken.
modules in /lib/modules/ for recovery access
FS OS Notes
/media /mnt — Mount points for removable/temp /srv Data served by system: /srv/http, /srv/ftp
filesystems ext4 Linux Journaled, extents, 1 EB max volume. Default Linux FS.
XFS Linux High-performance, great for large files & parallel I/O.
14. I/O & STORAGE
NTFS Windows Journaled, ACLs, compression, encryption (EFS).
I/O Methods APFS macOS/iOS Copy-on-write, snapshots, strong encryption, SSD-optimised.
Method How Best for FAT32 Universal No journaling, 4 GB file limit. Universal compatibility (USB).
Programmed I/O CPU polls device in a loop — wastes CPU Trivial/fast devices only
ZFS Linux/BSD CoW, built-in RAID, checksums, snapshots, self-healing.
Interrupt-driven CPU does other work; device interrupts when done Slow devices (keyboard, disk)
★ Inodes store ALL file metadata EXCEPT the filename. Filename lives only in directory entries.
DMA (Direct Memory Device transfers data to RAM without CPU; CPU Large transfers (disk, NIC, GPU)
Access) notified only on completion 15. MEMORY MANAGEMENT (ADVANCED)
★ DMA is critical for performance — without it, every disk byte transfer would stall the CPU.
Allocation Algorithms
Disk Scheduling (HDD) Algorithm Strategy Weakness
HDDs have mechanical seek time (arm moves to track) + rotational latency. OS reorders pending requests to minimise First Fit First hole ≥ requested size Leaves fragments at start of memory
total seek distance.
Best Fit Smallest sufficient hole Creates tiny unusable leftover fragments
Algorithm Strategy Note
Worst Fit Largest hole Wastes large blocks; leaves medium
FCFS Service in arrival order Simple; poor seek optimization fragments
SSTF Service closest track first Low seek; starvation of far requests Next Fit First fit from last allocation point Spreads fragmentation evenly
SCAN (Elevator) Sweep back and forth across disk Fair; predictable; good throughput Buddy System Split/merge power-of-2 blocks Internal fragmentation up to 50%; fast merge
LOOK Like SCAN but reverses at last request, not More efficient than SCAN ★ Linux kernel uses the Buddy System for page allocation and Slab allocator for kernel objects.
end
C-SCAN One-directional sweep; jump back to start Uniform wait; avoids SCAN bias Buddy System
Memory split into power-of-2 sized blocks. Allocation: find smallest fitting power-of-2; split if needed. Deallocation:
★ SSDs have no seek time — FCFS or simple queuing works. NVMe uses multiple parallel queues (65535 merge adjacent same-size 'buddies' recursively → fast coalescing. Used in Linux page allocator (/proc/buddyinfo
queues × 65535 cmds).
shows current state).
Slab Allocator Thread Pool Pattern
Pre-allocates caches of frequently used kernel objects (inodes, PCBs, dentries, sockets). Objects initialised once and Pre-create N worker threads. Tasks submitted to a shared queue. Workers pull and execute tasks. Avoids thread
recycled — eliminates init overhead and fragmentation for fixed-size objects. Three states: full (all objects allocated), creation overhead per task. Key parameters: pool size (usually = CPU cores for CPU-bound; larger for I/O-bound),
partial (some free), empty (all free → returned to buddy system). queue capacity (bounded to apply back-pressure).
✔ Eliminates per-object init overhead ✘ Memory not released until slab empty ✔ Reuses threads → no create/destroy overhead ✘ Fixed pool size may under/over-provision
✔ Near-zero fragmentation for fixed objects ✘ Complex implementation ✔ Bounded resource usage ✘ Queue full → must block or reject
✔ Fast allocation from partial slabs ✘ Per-object overhead in metadata ✔ Easy task submission API ✘ Hard to handle task priorities
★ mmap is how shared libraries are loaded — libc is mapped once physically and shared (read-only code Producer-Consumer with Channels
pages) across all processes. Producers put work into a buffered channel; consumers pull and process. Channel capacity controls back-pressure. If
full → producer blocks (or drops). If empty → consumer blocks. Decouples production rate from consumption rate.
16. CONCURRENCY PATTERNS Used in Go (goroutines + channels), Rust (mpsc), Java (BlockingQueue).
Memory Ordering & Fences ★ Rule of thumb: CPU-bound tasks → pool size = CPU cores. I/O-bound tasks → pool size = cores × (1 +
wait_time/compute_time).
CPUs and compilers reorder instructions for performance. In single-threaded code this is invisible. In multi-threaded
code it breaks assumptions. Memory barriers/fences prevent reordering across the barrier point.
17. LINUX INTERNALS
Order Type Meaning
LoadLoad No load reordered before another load CFS — Completely Fair Scheduler
Linux's default scheduler (since 2.6.23). Goal: every runnable process gets an equal share of CPU. Tracks vruntime
StoreStore No store reordered before another store
(virtual runtime — CPU time weighted by priority/nice value) per process. Always runs process with smallest vruntime.
LoadStore / StoreLoad Full barrier — most expensive; prevents all reordering Uses a red-black tree sorted by vruntime — O(log n) insert/delete, O(1) find-min.
acquire All subsequent reads/writes happen after this read nice values: -20 (highest priority) to +19 (lowest). Lower nice → vruntime accumulates slower → gets more CPU.
release All prior reads/writes happen before this write Preemption: on tick or wakeup, if current's vruntime > min vruntime + threshold → reschedule.
★ x86 has a strong memory model (TSO — Total Store Order). ARM/RISC-V are weakly ordered — need explicit ★ CFS guarantees fairness, not equal time slices. High-priority process just accumulates vruntime slower.
barriers.
cgroups (Control Groups)
Compare-And-Swap (CAS) & Lock-Free Kernel mechanism to limit, account, and isolate resource usage (CPU, memory, disk I/O, network) of process groups.
CAS: atomic instruction — if memory[addr] == expected, set it to new_value, return true; else return false. Foundation Hierarchical — nested groups. Used by: Docker (container resource limits), systemd (service limits), Kubernetes (pod
of all lock-free data structures. limits).
bool CAS(addr, expected, new_val): // atomic if *addr == expected: *addr = new_val; return true cgroup subsystem What it controls
else: return false
cpu / cpuacct CPU time allocation and accounting
Lock-free: at least one thread makes progress at all times (no deadlock possible). Wait-free: every thread makes
memory RAM + swap limits; OOM killer per cgroup
progress in bounded steps (stronger). Lock-free ≠ faster — high contention causes CAS retries that waste cycles (ABA
problem). blkio Block I/O bandwidth and IOPS limits
ABA Problem: CAS sees value A, another thread changes A→B→A. CAS succeeds incorrectly. Fix: version counter net_cls/net_prio Network packet classification and priority
(tagged pointer) — compare address AND version number together. pids Limit number of processes in group (fork bomb prevention)
Namespaces Out-of-Order Execution
Kernel feature that wraps global system resources in an abstraction — each namespace has its own isolated view. CPU doesn't execute instructions in program order — executes whichever is ready (inputs available). Uses a reorder
Foundation of containers. buffer (ROB) to track in-flight instructions and commit results in program order. Hides latency (e.g. cache miss stalls
one instruction while CPU executes others).
Namespace Isolates
pid Process IDs — container has its own PID 1
Pipeline stages: Front-end (fetch, decode, rename) → Issue queue (wait for operands) → Execution units → ROB →
Commit in order. Modern CPUs are 4–6 wide (issue 4–6 µops/cycle).
net Network interfaces, routes, iptables, sockets
★ Spectre/Meltdown (2018): exploited speculative OOO execution to leak data across security boundaries via
mnt Filesystem mount points — container's own /
cache timing side-channels.
uts Hostname and NIS domain name
Branch Prediction
ipc SysV IPC, POSIX message queues
CPU predicts direction of branches before they resolve to keep pipeline full. Static: always predict taken/not-taken.
user UID/GID mappings — root in container ≠ root on host
Dynamic: use history table (2-bit saturating counters) — learns patterns. Modern CPUs: 95–99% prediction accuracy.
cgroup cgroup root — hides host cgroup hierarchy Misprediction penalty: ~15–20 cycles (flush pipeline, restart from correct path).
★ Container = cgroups (resource limits) + namespaces (isolation) + overlay filesystem (layered images). No Indirect branch predictor: predicts target address (not just taken/not-taken) — used for virtual function calls, jump
hypervisor needed. tables. Return address stack: dedicated hardware stack for predicting function returns.
eBPF (Extended Berkeley Packet Filter) ✔ ~99% accuracy on regular loops and if-else ✘ Misprediction = 15–20 cycle penalty
Run sandboxed programs inside the Linux kernel without changing kernel source or loading modules. eBPF programs ✔ Hides branch latency completely when correct ✘ Spectre vulnerability — branch predictor exploitable
are JIT-compiled and verified for safety (no infinite loops, no invalid memory access). Attach to: network events,
NUMA (Non-Uniform Memory Access)
syscalls (kprobes/tracepoints), performance counters, LSM hooks.
Multi-socket servers: each CPU socket has local RAM. Accessing local RAM is fast (~100ns). Accessing another
Used by: Cilium (Kubernetes networking), Falco (security), bcc/bpftrace (observability), Facebook's network load socket's RAM crosses the interconnect (QPI/Infinity Fabric) — slower (~300ns). OS must be NUMA-aware to place
balancer (Katran). Enables zero-overhead tracing and custom kernel logic without reboots. processes near their memory. Linux: numactl, taskset.
★ eBPF is often called 'JavaScript for the kernel' — safe, dynamic, powerful kernel extensions without kernel Concept Detail
modules.
NUMA node CPU socket + its local RAM
Linux Boot & Init Local access Fast — same node
① UEFI/BIOS POST + hardware init. ② Bootloader (GRUB) loads kernel + initramfs into RAM. ③ Kernel decompresses Remote access ~2–3× slower — crosses interconnect
itself, initialises memory, CPU, scheduler, drivers. ④ Mounts initramfs as temporary root. ⑤ Pivots to real root
NUMA-aware alloc mmap with mbind(); numactl --membind
filesystem. ⑥ Launches systemd (PID 1). ⑦ systemd activates units (services, mounts, sockets) in dependency order.
⑧ Login prompt. False NUMA sharing Hot data on one node; all CPUs fight for it → remote access hell
★ systemd uses socket activation and parallel startup — much faster than SysV init's sequential script ★ On NUMA systems, memory locality can matter as much as cache locality. Profile with numastat.
execution.
CPU Registers & ISA
18. COMPUTER ARCHITECTURE General-purpose registers (x86-64): RAX, RBX, RCX, RDX, RSI, RDI, RSP (stack pointer), RBP (base pointer),
R8–R15. Special: RIP (instruction pointer), RFLAGS (condition codes). SIMD: XMM/YMM/ZMM registers
Instruction Pipeline (128/256/512-bit) for vectorised float/int operations (SSE/AVX).
Modern CPUs execute instructions in overlapping stages: Fetch → Decode → Execute → Memory → Write-back. RISC vs CISC: RISC (ARM, RISC-V) — simple fixed-size instructions, many registers, load/store architecture. CISC
Multiple instructions in-flight simultaneously (pipelined). Ideal: 1 instruction completes per cycle. A 5-stage pipeline can (x86) — complex variable-length instructions decoded into micro-ops internally. Modern x86 has a RISC core with
have 5 instructions in flight. CISC front-end.
Hazard Type Cause Fix
★ x86-64 calling convention: first 6 int args in RDI, RSI, RDX, RCX, R8, R9. Return value in RAX. Callee-saved:
Structural Two instructions need same HW resource Stall or duplicate hardware RBX, RBP, R12–R15.
Data Instruction needs result of previous instruction Forwarding (bypass) or stall (NOP bubbles)
Control (Branch) Next PC unknown until branch resolves Branch prediction; speculative execution
19. COMPILERS & LINKERS Compiler Optimisations
Optimisation What it does
Compilation Pipeline Constant folding Evaluate constant expressions at compile time: 3*8 → 24
Source → binary is a multi-stage process. Each stage transforms the representation.
Dead code elimination Remove code whose result is never used
Stage Tool Input→Output Key Action
Inlining Replace function call with function body — eliminates call overhead
Preprocessing cpp source.c → source.i Expand #include, #define, #ifdef macros
Loop unrolling Duplicate loop body N times — reduce branch/counter overhead
Compilation cc1 source.i → source.s Parse → AST → IR → optimise → assembly
Vectorisation (auto-SIMD) Use SIMD instructions (AVX) for loops over arrays automatically
Assembly as source.s → source.o Convert assembly to machine code + ELF relocations
Register allocation Keep hot variables in CPU registers, not stack
Linking ld *.o + libs → binary Resolve symbols, merge sections, fix addresses
Alias analysis Prove pointers don't alias → enable more aggressive reordering
ELF Format (Executable and Linkable Format) LTO (Link-Time Opt.) Optimise across compilation units at link time
Standard binary format on Linux/Unix. Contains:
GCC/Clang flags: -O0 (debug, no opt) -O2 (production default) -O3 (aggressive) -Os (size) -Ofast (ignore IEEE;
Section Contents dangerous) -flto (link-time opt)
.text Executable machine code (read-only)
★ Always profile before optimising. The compiler's -O2 is usually better than hand-tuned code. Premature
.data Initialized global/static variables optimisation is the root of all evil (Knuth).
.bss Uninitialised globals (just size stored; zeroed at load)
Position-Independent Code (PIC) & ASLR
.rodata Read-only data: string literals, const arrays PIC: code that works regardless of where it is loaded in memory. Uses relative addressing and GOT. Required for
.symtab /strtab — symbol table and string names (stripped in release) shared libraries (.so) and for ASLR. Slight overhead vs absolute addressing but negligible on modern CPUs.
.rel/.rela Relocation entries — addresses to fix up at link/load time ASLR (Address Space Layout Randomisation): OS loads stack, heap, libraries at random addresses each run. Makes
.plt/.got Procedure Linkage Table + Global Offset Table — for dynamic linking
buffer overflow exploits much harder (attacker can't predict addresses). Combined with NX bit (no-execute on data
pages) and stack canaries — modern exploit mitigations.
.debug_* DWARF debug info (line numbers, variable names, types)
★ ASLR + NX + Stack canary = the holy trinity of memory safety. All enabled by default in modern
★ Strip debug symbols (strip binary) for release — can reduce binary size 10×. Use separate .dSYM / .debug Linux/macOS/Windows.
files for crash analysis.
★ Dynamic linking saves RAM (shared .so pages) but introduces 'dependency hell'. Docker solves this by
bundling libs in container.