Operating Systems
Subject 2 of 10 Β· from what an OS does to how disks are scheduled, with the formulas and algorithms that show up on exams.
1. OS Basics
Functions of an operating system
- Multiprogramming: keep the CPU busy by loading several processes into memory. When one blocks on I/O, the CPU switches to another instead of idling.
- Time sharing: give each process short CPU slices so interactive users all feel served at once.
- Resource management: arbitrate CPU, memory, and I/O between competing processes.
- Protection and security: stop processes from interfering with each other or the kernel.
P = probability a process waits on I/O, n = degree of multiprogramming
Example: each process spends 80% of its time waiting on I/O (P = 0.8) with 3 processes in memory: utilization = 1 β 0.8Β³ = 1 β 0.512 β 49%. Add more processes and utilization climbs β until thrashing (chapter 7) ruins it.
Security goals: the CIA triad
| Goal | Meaning |
|---|---|
| Confidentiality | Only authorized users can read the data |
| Integrity | Data can't be modified by unauthorized users |
| Availability | The system stays up for legitimate users |
Authentication verifies who you are (passwords, biometrics). Authorization decides what you're allowed to do once inside.
Classic security threats
| Threat | What it is |
|---|---|
| Trojan Horse | Looks like useful software, hides a malicious payload |
| Trapdoor / Backdoor | Secret entry point that bypasses authentication |
| Logic Bomb | Malicious code that triggers on a specific date or condition |
| Virus | Needs a host program; replicates when the host runs |
| Worm | Standalone; self-replicates across networks without a host |
2. Processes & Threads
Program vs process
A program is a passive entity on disk. A process is a program in execution β an active entity with its own address space and system state.
Process memory layout
| Section | Holds |
|---|---|
| Text | The program's code (read-only) |
| Data | Global and static variables |
| Heap | Dynamically allocated memory (grows upward) |
| Stack | Function calls, local variables (grows downward) |
Process Control Block (PCB)
The OS's file on every process: process ID (PID), parent PID, current state, program counter, CPU register snapshot, scheduling info (priority), accounting info (CPU time used), memory-management info, I/O status, and the list of open files.
Process states
New β Ready β Running β Terminated, with a detour: a running process that waits on I/O goes to Waiting, then back to Ready when the I/O completes. Suspended variants (suspend-ready, suspend-waiting) exist when the OS swaps processes out to disk.
Context switching
Switching the CPU from one process to another: save the old process's registers and program counter into its PCB, load the next process's state. It's pure overhead β the CPU does no useful work during the switch β which is why dispatch latency matters.
Threads
A thread is a lightweight process: it shares the address space, code, data, and open files with its sibling threads, carrying only its own stack, registers, and program counter. Benefits of multithreading: responsiveness (UI stays alive during long work), resource sharing, economy (creating a thread is far cheaper than a process), and scalability across cores.
| Aspect | User-level threads | Kernel-level threads |
|---|---|---|
| Managed by | Thread library in user space | The OS kernel |
| Context switch | Fast β no kernel trap | Slower β kernel involved |
| Blocking | One thread blocks, all block | One thread blocks, rest continue |
Multithreading models
| Model | Mapping | Trade-off |
|---|---|---|
| Many-to-One | Many user β one kernel thread | Efficient, but one blocking call stalls everything |
| One-to-One | Each user β its own kernel thread | True concurrency, but creating threads costs more |
| Many-to-Many | Multiplexed | Best of both; the OS can run the right number of kernel threads |
Inter-Process Communication (IPC)
- Shared memory: processes read/write a common region. Fast, but they must synchronize access themselves.
- Message passing: send() / receive() primitives. Direct names the other process; indirect goes through a mailbox/port. Synchronous (blocking) vs asynchronous (non-blocking).
3. CPU Scheduling
The short-term scheduler picks which ready process gets the CPU next. The dispatcher performs the actual handoff; dispatch latency is the time a switch takes.
Scheduling criteria
| Criterion | Goal |
|---|---|
| CPU utilization | Keep the CPU as busy as possible |
| Throughput | Maximize processes completed per unit time |
| Turnaround time | Minimize submission β completion |
| Waiting time | Minimize time spent in the ready queue |
| Response time | Minimize submission β first response (interactive systems) |
The algorithms
| Algorithm | Preemptive? | How it picks |
|---|---|---|
| FCFS | No | First come, first served β simple, but convoy effect |
| SJF | No | Shortest burst first β optimal avg waiting time, can starve long jobs |
| SRTF | Yes | Preemptive SJF β picks shortest remaining time |
| Priority | Both | Highest priority first β starvation fixed with aging |
| Round Robin | Yes | Fixed time quantum per process, cyclically β the time-sharing workhorse |
| Multilevel Queue | Yes | Separate queues per class (foreground/background), fixed priority between queues |
| Multilevel Feedback Queue | Yes | Processes move between queues based on behavior β the most general scheme |
Quantum too large β degenerates to FCFS. Too small β context-switch overhead eats the CPU. A common rule of thumb: quantum long enough that 80% of bursts finish within one slice.
Queues and schedulers
- Job queue β every process in the system. Ready queue β in memory, ready to run. Device queues β waiting on a particular I/O device.
- Long-term scheduler β admission control; sets the degree of multiprogramming. Short-term β the CPU scheduler itself, runs constantly. Medium-term β swapping; moves processes in/out of memory.
4. Process Synchronization
When processes share data, the critical section β the code touching shared state β needs guarding. Any correct solution must provide:
- Mutual exclusion β only one process inside at a time
- Progress β a waiting process can't be blocked forever by processes outside
- Bounded waiting β no starvation; every waiter eventually gets in
Peterson's solution (two processes)
A pure-software solution using a flag[2] array (who wants in) and a turn variable (whose turn it is). It satisfies all three requirements but only works for two processes and assumes atomic loads/stores.
// process i (j is the other)
flag[i] = true;
turn = j;
while (flag[j] && turn == j) ; // busy wait
// --- critical section ---
flag[i] = false;
// --- remainder ---
Hardware support: Test-and-Set
boolean TestAndSet(boolean *lock) {
boolean old = *lock;
*lock = true; // executed atomically
return old;
}
// usage: while (TestAndSet(&lock)) ; // critical section; lock = false;
Semaphores
An integer variable touched only through two atomic operations: wait() (P β decrement, block if zero) and signal() (V β increment, wake a waiter).
- Binary semaphore (values 0/1) β behaves like a mutex.
- Counting semaphore β tracks multiple identical resources (e.g. 3 printers free).
Mutex locks vs spinlocks
A mutex is acquire/release around the critical section. A spinlock is a mutex that busy-waits instead of sleeping β wasteful on single-core, but the fastest option for very short critical sections on multicore.
Monitors
A high-level construct: only one process may be active inside a monitor at a time, enforced by the compiler/runtime. Condition variables (wait/signal) let processes sleep inside until some condition holds β the classic bounded-buffer implementation.
Classic synchronization problems
| Problem | Setup | Key idea |
|---|---|---|
| ProducerβConsumer | Bounded buffer between producer and consumer | Semaphores empty, full, and mutex |
| ReadersβWriters | Many readers OK together; writers need exclusivity | First reader locks out writers; variants favor readers or writers |
| Dining Philosophers | 5 philosophers, 5 chopsticks, one between each pair | Naive pickup deadlocks β fix with ordering or an asymmetric pickup rule |
5. Deadlocks
A deadlock is a standstill: each process in a set waits for a resource held by another process in the set, and nobody can move. All four conditions must hold simultaneously:
| # | Condition | Meaning |
|---|---|---|
| 1 | Mutual exclusion | At least one resource is non-sharable |
| 2 | Hold and wait | A process holds resources while waiting for more |
| 3 | No preemption | Resources can't be forcibly taken away |
| 4 | Circular wait | P1 waits on P2, P2 waits on P3, β¦, Pn waits on P1 |
Resource Allocation Graph (RAG)
Processes are circles, resource types are squares. A request edge PβR means "waiting for"; an assignment edge RβP means "holding". With single-instance resources, a cycle in the graph is a deadlock; with multiple instances, a cycle only means deadlock might exist.
Handling strategies
- Prevention β break one of the four conditions: spooling (breaks mutual exclusion for printers), request-everything-upfront (breaks hold-and-wait), allow preemption, or impose a global resource ordering (breaks circular wait).
- Avoidance β the Banker's algorithm: grant a request only if the system stays in a safe state, i.e. some order (a safe sequence) exists in which every process can still finish. Needs each process's maximum claim upfront. Key relation: Need = Max β Allocation.
- Detection & recovery β let deadlocks happen, find them with a wait-for graph (single-instance) or detection algorithm, then recover by killing processes or preempting resources.
Prevention negates a condition statically. Avoidance dynamically checks each allocation. Detection does nothing until a deadlock actually forms.
6. Memory Management
The CPU generates logical addresses; RAM speaks physical addresses. The MMU translates between them at runtime, which is what lets every process believe it owns contiguous memory starting at zero.
- Dynamic loading β a routine is loaded only when actually called. Better memory use, but the programmer (or linker) decides what loads when.
- Dynamic linking β linking is postponed to execution time. Shared libraries (.dll, .so) live once in memory and serve every process.
Contiguous allocation
Each process gets one unbroken block of memory. Simple, but suffers external fragmentation β free memory scattered in holes too small to use. Classic placement policies:
| Policy | Chooses | Trade-off |
|---|---|---|
| First Fit | The first hole big enough | Fast; litters small holes at the front |
| Best Fit | The smallest hole big enough | Least waste per pick, but slowest search |
| Worst Fit | The largest hole | Leaves big leftovers; rarely the winner |
Partitioning
- Fixed partitioning β memory split into fixed-size regions. Simple, but wastes space inside each partition (internal fragmentation).
- Variable partitioning β partitions sized to each process. No internal waste, but external fragmentation appears over time; compaction (shuffling everything together) fixes it at a cost.
Internal = wasted space inside an allocated block (you got more than you asked for). External = wasted space between blocks (enough total free memory, but no single hole fits).
Paging
Memory is chopped into fixed-size frames; each process is chopped into same-size pages. Pages land in any free frames β no contiguity needed, so external fragmentation disappears. A page table maps page β frame per process.
- Multilevel paging β page the page table itself, so huge address spaces don't need one giant table in RAM.
- Inverted page table β one table for all of physical memory (one entry per frame), with process IDs attached. Smaller, but lookups need hashing.
- Internal fragmentation remains: the last page of a process is rarely full.
Segmentation
Memory is divided by logical meaning β code, data, stack segments of variable size β matching how programmers think. Each segment gets a base + limit in a segment table.
| Paging | Segmentation | |
|---|---|---|
| Division | Fixed-size, physical | Variable-size, logical |
| Fragmentation | Internal (last page) | External (variable holes) |
| Programmer visibility | Invisible | Visible β matches program structure |
7. Virtual Memory
Virtual memory lets processes use more memory than physically exists: only the pages actually needed live in RAM, the rest wait on disk. Benefits: larger effective address spaces, higher multiprogramming, less I/O per process.
Demand paging
Pages are loaded only on first use β a page fault triggers the load. A valid/invalid bit in the page table marks which pages are resident. Pure demand paging starts with nothing loaded; locality keeps fault rates low after warmup.
Page replacement
When RAM is full and a fault needs a frame, pick a victim page:
| Algorithm | Victim = | Notes |
|---|---|---|
| FIFO | Oldest loaded page | Simple; can suffer Belady's anomaly (more frames β more faults!) |
| Optimal | Page used farthest in the future | Theoretical lower bound β impossible in practice, used as benchmark |
| LRU | Least recently used | Excellent approximation of Optimal; needs timestamp or stack hardware |
| Clock / Second-chance | FIFO with a reference bit | Cheap LRU approximation used in real systems |
| LFU / MFU | Least / most frequently used | Counting-based; rarely used alone |
A TLB miss costs an extra memory lookup for the page table itself.
Thrashing
When the degree of multiprogramming is too high, processes spend all their time page-faulting instead of executing β CPU utilization collapses even though everyone is "busy". Fixes: reduce multiprogramming, give processes bigger working sets, add RAM. The working-set model tracks each process's current locality to size allocations correctly.
8. File Systems
File concepts and attributes
A file is a named collection of related data on secondary storage. Its metadata: name, type, size, location on disk, protection/permissions, owner, and timestamps.
Allocation methods
| Method | How it stores | Strengths / weaknesses |
|---|---|---|
| Contiguous | One unbroken run of blocks | Fast sequential + direct access; external fragmentation |
| Linked | Blocks chained with pointers | No fragmentation; terrible random access |
| Indexed | An index block lists all data blocks | Direct access without fragmentation; index overhead |
Free-space management
- Bit vector β one bit per block. Compact, fast to scan.
- Linked list β free blocks chained together.
- Counting β store (first-free-block, count) pairs for runs of free space.
9. Disk Scheduling
Avg rotational delay = half a revolution (e.g. 7200 RPM β 8.33 ms/rev β 4.17 ms avg)
Seek time dominates for random access; rotational delay matters most for sequential access. Example: seek 6 ms + rotational 4.17 ms + transfer 0.5 ms = 10.67 ms per access.
The algorithms
| Algorithm | Head behavior | Notes |
|---|---|---|
| FCFS | Services in arrival order | Fair; wild seeks, no optimization |
| SSTF | Nearest request first | Good throughput; edge requests can starve |
| SCAN (elevator) | Sweeps to the disk end, then reverses | No starvation; fairer than SSTF |
| C-SCAN | Sweeps one direction only, jumps back | Uniform wait times; the return trip is pure overhead |
| LOOK | Like SCAN, but reverses at the last request | Skips pointless travel to empty disk edges |
Worked example β requests at tracks 98, 183, 37, 122, 14, 124, 65, 67, head at 53 moving outward: SCAN services 65 β 67 β 98 β 122 β 124 β 183, then reverses to 37 β 14. C-SCAN instead jumps straight back to 0 after 183 and continues 14 β 37. LOOK would reverse at 183 without ever visiting the disk edge.