Operating Systems Concepts
Operating Systems Concepts
An operating system is the software that mediates between applications and hardware. It provides the abstractions — processes, files, sockets, virtual memory — that make hardware programmable at scale. Understanding how it works makes you a dramatically better programmer.
Processes and Threads
A process is a running program: it has its own virtual address space, file descriptors, heap, and stack. The OS creates an illusion of dedicated hardware for each process through multiplexing.
A thread is a unit of execution within a process. All threads in a process share the heap and file descriptors but have separate stacks and register state. Threads are lighter than processes — creating one is cheaper, and context switching between threads in the same process is faster.
Context switching is the overhead of multitasking. The OS saves the current CPU state (registers, program counter) to the process control block (PCB), then restores another process's state. On modern hardware this costs ~1-10 microseconds, which is why you pay for hundreds of threads but not hundreds of processes on a single CPU.
Process states: running (currently on CPU), ready (waiting for CPU), blocked (waiting for I/O or event), zombie (finished but not reaped by parent). The OS scheduler moves processes between these states.
Scheduling
The scheduler decides which ready process runs next. Key metrics: throughput (processes completed per second), turnaround time (total time from submission to completion), response time (time until first response), fairness.
First-Come-First-Served (FCFS): processes run in arrival order. Simple, but a long process starves all subsequent short ones — the convoy effect.
Shortest Job First (SJF): minimizes average turnaround time, but requires knowing execution times in advance (impractical for interactive systems).
Round Robin: each process gets a fixed time quantum (typically 1-100ms), then is preempted. Good response time, higher context switch overhead. The quantum size matters: too small wastes time context switching, too large degrades to FCFS.
Priority Scheduling: processes have priorities; the highest-priority ready process runs. Risk of starvation for low-priority processes, mitigated by aging (raising priority as wait time increases).
Completely Fair Scheduler (CFS): Linux's scheduler tracks each process's "virtual runtime" and always schedules the one with the smallest, ensuring proportional CPU time allocation.
Memory Management
Each process has a virtual address space — typically 4GB on 32-bit, vastly larger on 64-bit. Virtual addresses are translated to physical addresses by the Memory Management Unit (MMU) using page tables.
Memory is divided into fixed-size pages (typically 4KB). The page table maps virtual page numbers to physical frame numbers. When a page is accessed that isn't in RAM, a page fault occurs and the OS loads it from swap. If swap is on SSD, this takes ~100μs; on HDD, ~10ms — 1000x and 100,000x slower than RAM access, respectively.
Translation Lookaside Buffer (TLB): a hardware cache for page table entries. Because most programs exhibit temporal and spatial locality, TLB hit rates exceed 99%, making virtual memory translation nearly free in practice.
Memory allocation: the kernel allocates physical pages to processes and manages demand paging (loading pages only when accessed). Page replacement algorithms (LRU, Clock) decide which pages to evict when RAM is full.
Concurrency and Synchronization
Multiple threads accessing shared data concurrently creates race conditions: the outcome depends on the interleaving of operations. A race condition is a bug that may appear rarely in testing but catastrophically in production.
Mutual exclusion (mutex) ensures only one thread enters a critical section at a time. A mutex has two states: locked and unlocked. Acquiring a locked mutex blocks the thread until it's released.
Semaphores generalize mutexes: a counting semaphore allows up to N simultaneous acquirers. Used to limit concurrent access to a resource pool (e.g., at most 10 threads can use a database connection at once).
Condition variables let threads wait until a condition is true without busy-waiting. The pattern: hold a mutex, check condition, wait (atomically releases mutex and sleeps), recheck on wake.
Deadlock requires four conditions simultaneously: mutual exclusion, hold-and-wait, no preemption, circular wait. Prevention strategies include lock ordering (always acquire locks in the same order) or using try-lock with timeout.
File Systems
The OS provides a file system abstraction over block storage: files, directories, and permissions. Underneath, data is stored in blocks on disk. A filesystem manages the mapping: inodes (Unix) or MFT entries (NTFS) store metadata (size, permissions, timestamps) and block pointers.
Journaling (ext4, NTFS) logs pending operations before committing them, enabling recovery after crashes without full disk scans. Copy-on-write filesystems (ZFS, btrfs) never overwrite data — they write to new blocks and update pointers — making snapshots instant and crashes safe.
System Calls
Applications interact with the OS through system calls — controlled transitions from user mode to kernel mode. open(), read(), write(), close() for files; fork(), exec() for processes; socket(), connect(), send() for networking. Each system call has significant overhead (mode switch, argument validation, kernel work) — batching I/O is one of the most effective performance optimizations.