Outline
Definition
An Operating System (OS) is a layer of software that manages hardware resources and provides abstractions to user programs. It serves two fundamental roles:
- Resource Manager — multiplexes CPU, memory, I/O devices among competing processes.
- Extended Machine — hides hardware complexity behind clean, portable interfaces (system calls).
Tips
Process Management, Memory Management, File System, Device Management.
Concurrency, Sharing, Virtualization, Asynchronism.
Architecture
The OS kernel runs in supervisor mode (kernel mode), with unrestricted access to hardware; user programs run in user mode, confined to their own virtual address space. The boundary is crossed via system calls and interrupts.
| Component | Function |
|---|---|
| Process Manager | Create, schedule, and terminate processes; inter-process communication (IPC) |
| Memory Manager | Allocate and reclaim memory; virtual memory with paging/segmentation |
| File System | Organize persistent storage into files and directories |
| I/O Subsystem | Abstract device access via a uniform interface; buffering, caching, spooling |
| Network Stack | Protocol implementation (TCP/IP), socket interface |
Philosophy
- Abstraction v.s. Performance: higher-level interfaces simplify programming but introduce overhead.
- Protection v.s. Sharing: isolate processes for safety, yet allow efficient communication.
- Fairness v.s. Throughput: scheduler must balance individual response time with overall system utilization.
Process & Thread
Process
A process is a program in execution. It consists of:
- Code (text segment) — executable instructions.
- Data — global/static variables.
- Heap — dynamically allocated memory.
- Stack — function call frames, local variables.
- PCB (Process Control Block) — metadata: PID, state, registers, memory maps, open file descriptors.
Process State Diagram:
┌──────────┐
│ Created │
└─────┬─────┘
│ admitted
▼
┌──────────┐ scheduler dispatch ┌──────────┐
│ Ready │ ───────────────────► │ Running │
└─────┬─────┘ ◄─────────────────── └─────┬─────┘
│ interrupt / yield │
│ │ I/O or event wait
│ ┌──────────┐ │
└─────────│ Waiting │◄────────────┘
└──────────┘ exit → Terminated
- Ready → Running: chosen by the scheduler (dispatch).
- Running → Ready: preempted (timer interrupt) or voluntarily yields.
- Running → Waiting: blocks on I/O or an event.
- Waiting → Ready: I/O or event completes.
Thread
A thread is the unit of CPU scheduling. Threads within the same process share:
- Code, data, heap, open files — all global resources.
- Each has its own stack, registers, and PC (program counter).
| Aspect | Process | Thread |
|---|---|---|
| Address space | Independent | Shared within process |
| Context switch cost | High (TLB flush, MMU switch) | Low (same page table) |
| Communication | IPC (pipe, message, shared memory) | Direct read/write to shared memory |
| Fault isolation | Strong (one crash won’t affect others) | Weak (one thread crash → entire process dies) |
Kernel-level Threads (KLT) v.s. User-level Threads (ULT):
- ULT: managed entirely in user space by a threading library. Fast creation/switching (no kernel involvement), but one blocking call blocks the entire process.
- KLT: managed by the kernel. Blocking is per-thread, but switching requires a mode switch.
- Hybrid: multiple KLTs mapped from multiple ULTs (e.g., Go’s goroutines on OS threads).
Context Switch
A context switch is the mechanism by which the CPU switches from one process/thread to another.
Steps:
- Save the current process’s state (registers, PC, stack pointer) into its PCB.
- Load the next process’s state from its PCB.
- Switch memory mappings (page table base register).
- Flush the TLB and pipeline if necessary.
The cost is pure overhead — no useful work is done during the switch. Typical latency: 1–10 μs.
CPU Scheduling
Scheduling Metrics
| Metric | Formula | Goal |
|---|---|---|
| Turnaround time | $T_{\text{completion}} - T_{\text{arrival}}$ | Minimize |
| Waiting time | $\sum (\text{time in ready queue})$ | Minimize |
| Response time | $T_{\text{first run}} - T_{\text{arrival}}$ | Minimize (interactive) |
| Throughput | $\frac{\text{\# jobs}}{\text{total time}}$ | Maximize |
Scheduling Algorithms
FCFS (First-Come, First-Served) — non-preemptive. Simple but suffers from the convoy effect: short jobs queue behind long jobs, inflating average waiting time.
SJF (Shortest Job First) — non-preemptive, selects the process with the smallest burst time. Provably optimal for minimizing average waiting time. However, requires knowing future CPU bursts.
SRTF (Shortest Remaining Time First) — preemptive version of SJF. Whenever a new process arrives with a shorter remaining time than the currently running one, the CPU is preempted.
Round Robin (RR) — each process gets a fixed time quantum $q$. After $q$ expires, it’s preempted and placed at the tail of the ready queue. If there are $n$ processes, each waits at most $(n-1)q$ before its next turn.
- $q$ large → degenerates to FCFS.
- $q$ small → many context switches, high overhead.
- Typical $q$: 10–100 ms.
Priority Scheduling — each process has a priority; the CPU is allocated to the highest-priority ready process. May lead to starvation: low-priority processes never run. Solution: aging — gradually increase priority over time.
Multilevel Feedback Queue (MLFQ) — the BSD/Linux approach:
- Multiple queues with different priorities.
- High-priority queues have short time slices (for interactive jobs).
- Low-priority queues have long time slices (for CPU-bound jobs).
- Processes migrate between queues based on observed behavior.
- Periodically, all processes are boosted to the top queue (prevents starvation).
Preemptive v.s. Non-Preemptive
| Preemptive | Non-Preemptive | |
|---|---|---|
| Interrupt running process? | Yes | No |
| Overhead | Higher (more switches) | Lower |
| Responsiveness | Good | Poor |
| Race conditions | More likely | Less likely |
Synchronization
The Critical Section Problem
A critical section is a code segment that accesses shared resources and must not be executed concurrently by multiple threads. The solution must satisfy:
- Mutual Exclusion: at most one thread in its critical section at any time.
- Progress: if no thread is in the critical section and some threads want to enter, the selection of the next entrant cannot be postponed indefinitely.
- Bounded Waiting: a thread that requests entry must eventually be granted it (no starvation).
Locks
A mutex (mutual exclusion lock) provides two atomic operations:
| |
Spinlock — the waiting thread spins in a loop while (test_and_set(&lock)), consuming CPU cycles. Efficient only for very short critical sections.
Hardware Support: Atomic Instructions
Test-and-Set (atomic RMW — Read-Modify-Write):
| |
Compare-and-Swap (CAS):
| |
These are the building blocks of lock-free data structures.
Semaphores
A semaphore $S$ is an integer variable accessed only through two atomic operations:
- $P$ (wait / down / proberen):
while (S <= 0); S--; - $V$ (signal / up / verhogen):
S++;
If $S$ counts resources, it’s a counting semaphore. If $S \in \{0, 1\}$, it’s a binary semaphore (equivalent to a mutex with ownership tracking).
Classic use: the Producer-Consumer problem with a bounded buffer:
| |
Monitors
A monitor is a high-level synchronization construct (language-level, e.g., Java’s synchronized) that encapsulates shared data, operations, and synchronization into a single module. Only one thread may be active inside a monitor at a time.
Condition variables allow threads to wait for a condition inside a monitor:
wait(cond)— releases the monitor lock and blocks.signal(cond)— wakes one waiting thread.broadcast(cond)— wakes all waiting threads.
Hoare semantics:
signalimmediately transfers the monitor to the awakened thread.
Mesa semantics (Java, pthreads): the signaler continues; the awakened thread must re-check the condition upon wake-up (usewhileinstead ofif).
Deadlock
Necessary Conditions (Coffman, 1971)
All four must hold simultaneously for a deadlock to occur:
- Mutual Exclusion: resources cannot be shared.
- Hold and Wait: a process holds some resources while waiting for others.
- No Preemption: resources cannot be forcibly taken away.
- Circular Wait: a cycle exists in the resource allocation graph.
Resource Allocation Graph
- Process $P_i$ → Resource $R_j$ (request edge): $P_i$ is waiting for $R_j$.
- Resource $R_j$ → Process $P_i$ (assignment edge): $R_j$ is allocated to $P_i$.
- A cycle in the graph with single-instance resources iff deadlock.
- With multi-instance resources, a cycle is necessary but not sufficient.
Handling Strategies
| Strategy | Approach | Cost |
|---|---|---|
| Prevention | Eliminate one of the four necessary conditions statically | Under-utilization |
| Avoidance | Dynamically check each request; grant only if safe state persists | Runtime overhead |
| Detection & Recovery | Let it happen, detect cycles, then abort or rollback | Recovery cost |
| Ostrich Algorithm | Ignore it (used by most general-purpose OSes) | Low |
Banker’s Algorithm (Avoidance)*
Maintains:
- Available $[m]$: available instances of each resource type.
- Max $[n \times m]$: maximum demand of each process.
- Allocation $[n \times m]$: currently allocated.
- Need $[n \times m]$ = Max − Allocation.
A state is safe if there exists a sequence $\langle P_{i_1}, \dots, P_{i_n} \rangle$ such that for each $P_{i_k}$, its remaining need can be satisfied by currently available resources plus those held by preceding $P_{i_j}$.
When $P_i$ requests resources, the algorithm:
- Temporarily grants the request.
- Checks if the resulting state is safe.
- If safe → grant; if unsafe → deny and block $P_i$.
Complexity: $O(m \cdot n^2)$ per request.
Memory Management
Address Binding
| Stage | Binding | Mechanism |
|---|---|---|
| Compile time | Absolute addresses | Compiler knows where the process will reside |
| Load time | Relocatable → absolute | Loader translates at load time |
| Execution time | Virtual → physical | MMU translates on every memory access |
Logical v.s. Physical Address
- Logical (virtual) address: generated by the CPU; what the process sees.
- Physical address: actual location in RAM.
- The MMU (Memory Management Unit) maps logical to physical at runtime:
with bounds checking: $0 \le \text{Logical Address} < \text{Limit}$.
Fragmentation
| Type | Description |
|---|---|
| External fragmentation | Free memory is split into many small holes; total free space is enough but no single block is large enough |
| Internal fragmentation | Allocated block is larger than requested; the unused sliver inside is wasted |
Compaction — move allocated blocks to coalesce free space. Costly, and only possible with dynamic relocation.
Paging
Paging divides physical memory into fixed-size frames and logical memory into same-sized pages. The mapping is maintained in a page table.
$$\text{Physical Address} = \text{Frame} \times \text{Page Size} + \text{Offset}$$where $\text{Frame} = \text{PageTable}[\text{Page Number}]$.
Page Table Entry (PTE) typically contains:
| Frame Number | Valid | Protection | Dirty | Referenced |
|---|
Translation Lookaside Buffer (TLB)
The page table is in memory — every data access would require two memory references (page table, then data). The TLB is a small, fast, fully-associative hardware cache for PTEs.
Effective Access Time:
$$EAT = \big(1 - h_{\text{TLB}}\big) \cdot (T_{\text{mem}} + T_{\text{mem}}) + h_{\text{TLB}} \cdot (T_{\text{TLB}} + T_{\text{mem}})$$where $h_{\text{TLB}}$ is the TLB hit rate (typically > 99%).
Multi-level Page Tables
A single page table for a 64-bit address space would be enormous (e.g., $2^{52}$ entries for 4 KB pages). Multi-level paging resolves this by hierarchically indexing:
- The outer page table maps large chunks; only populated subtrees are allocated.
- x86-64 uses 4 levels: PML4 → PDPT → PD → PT → Offset.
Inverted Page Table
Instead of one entry per virtual page, the inverted table has one entry per physical frame, indexed by (PID, page number). Searched via a hash table. Used by PowerPC and some IA-64 implementations.
Segmentation
Segmentation divides memory into variable-sized segments (code, data, stack). Each segment has a base and limit.
$$\text{Physical Address} = \text{Segment Base} + \text{Offset}, \quad 0 \le \text{Offset} < \text{Limit}$$Segmentation matches the programmer’s view of memory but suffers from external fragmentation. Many systems combine segmentation + paging (e.g., x86 in protected mode).
Virtual Memory
Virtual memory decouples the address space from physical memory, enabling:
- Programs larger than physical memory.
- Isolation: each process has its own virtual address space.
- Efficient sharing (e.g., shared libraries mapped into multiple processes).
Demand Paging
Pages are loaded into memory on demand — only when accessed.
- Page fault: CPU accesses a page not in memory → trap to OS.
- OS selects a victim frame (if memory is full), swaps it out, loads the requested page, updates the page table, and restarts the faulting instruction.
Page Replacement Algorithms
When memory is full and a page fault occurs, the OS must evict a page. The goal: minimize the page-fault rate.
| Algorithm | Policy | Notes |
|---|---|---|
| FIFO | Evict oldest-loaded page | Simple; suffers from Belady’s anomaly (more frames → more faults) |
| Optimal (OPT) | Evict page that will not be used for the longest time in the future | Theoretical lower bound; requires future knowledge |
| LRU | Evict least recently used page | Approximates OPT using the past; expensive to implement perfectly |
| Clock (Second Chance) | Circular scan; clear reference bit, evict pages with ref=0 | Practical approximation of LRU; used in most OSes |
Belady’s Anomaly: with FIFO, increasing the number of frames can paradoxically increase the page-fault rate. This cannot happen with stack algorithms (LRU, OPT).
Working Set Model
The working set $W(t, \Delta)$ is the set of pages referenced during the last $\Delta$ memory references at time $t$. The OS should allocate enough frames to hold the working set; if $\sum |W_i| > \text{total frames}$, thrashing occurs — the system spends more time paging than executing.
Working set clock (WSClock) — an efficient implementation combining the clock algorithm with working set tracking.
Thrashing
Thrashing: a process is spending more time swapping pages in and out than executing. Detection:
- Monitor page-fault rate: if it exceeds a threshold, the process needs more frames.
- If total demand exceeds available memory, the OS must suspend (swap out) some processes — load control.
File System
File Abstraction
A file is a named, persistent collection of data. The OS provides:
- Naming: hierarchical directory structure.
- Access methods: sequential, random (direct), indexed.
- Protection: owner, group, world permissions (rwx).
Directory Structure
/ (root)
├── bin/
├── etc/
├── home/
│ └── alice/
│ └── notes.md
├── usr/
└── var/
- Single-level — all files in one directory (naming conflicts).
- Two-level — per-user directories.
- Tree-structured — arbitrary depth; most modern OSes.
- Acyclic graph — allows sharing via links (hard links / symbolic links).
- General graph — cycles possible; garbage collection needed.
File Allocation Methods
| Method | Description | Pros/Cons |
|---|---|---|
| Contiguous | Each file occupies a contiguous set of blocks | Fast sequential access; external fragmentation; hard to grow |
| Linked | Blocks linked as a list; each block points to the next | No external fragmentation; slow random access; unreliable if a pointer is lost |
| FAT (File Allocation Table) | Linked list stored in a separate table (the FAT) | Random access via FAT scan; FAT must be in memory |
| Indexed (inode) | Per-file index block contains pointers to data blocks | Good random and sequential access; wastes space for small files |
Inode Structure (Unix)
The inode (index node) stores metadata and block pointers:
- File type, permissions, owner, timestamps.
- Direct blocks: first 12 block pointers point directly to data blocks.
- Single indirect: points to a block of block pointers.
- Double indirect: points to a block of single indirect pointers.
- Triple indirect: for very large files.
For a 4 KB block with 4-byte pointers, the maximum file size:
$$\text{Max size} = 12 \cdot 4\text{K} + \frac{4\text{K}}{4} \cdot 4\text{K} + \left(\frac{4\text{K}}{4}\right)^2 \cdot 4\text{K} + \left(\frac{4\text{K}}{4}\right)^3 \cdot 4\text{K}$$Free Space Management
- Bit vector: each block represented by 1 bit (free/allocated). Efficient but large.
- Linked list: free blocks form a list. Slow traversal.
- Grouping: store addresses of $n$ free blocks in the first free block.
- Counting: store
(start_block, count)for contiguous free runs.
Journaling
Write-ahead logging to ensure file system consistency after a crash:
- Write intended changes to a journal (log).
- Commit the journal entry.
- Checkpoint: apply changes to the actual file system.
- On crash recovery: replay committed journal entries, discard uncommitted ones.
Linux ext4 supports three journaling modes: journal (data + metadata), ordered (metadata only, data first), writeback (metadata only, no ordering).
I/O Management
I/O Architecture
User Process
│ system call
▼
Device-Independent OS Software
│
▼
Device Driver ─── Device Controller ─── Device
- Device controller: hardware interface (registers, DMA).
- Device driver: OS kernel module that talks to a specific controller.
- Device-independent layer: buffering, caching, naming, protection.
I/O Methods
| Method | Description | CPU involvement |
|---|---|---|
| Programmed I/O | CPU polls device status register in a loop | High (busy waiting) |
| Interrupt-driven I/O | Device raises an interrupt when ready; CPU handles it in ISR | Medium |
| DMA (Direct Memory Access) | DMA controller transfers data between device and memory; CPU only involved at start and end | Low |
Disk Scheduling
For a moving-head disk, access time = seek time + rotational latency + transfer time. Seek time dominates.
| Algorithm | Policy | Notes |
|---|---|---|
| FCFS | Service requests in arrival order | Fair but poor performance |
| SSTF (Shortest Seek Time First) | Select request closest to current head position | May cause starvation |
| SCAN (Elevator) | Head sweeps back and forth; services requests in one direction | Uniform wait time |
| C-SCAN | Like SCAN but services only in one direction; jumps back to start | More uniform than SCAN |
| LOOK / C-LOOK | SCAN/C-SCAN variants that reverse direction only when no requests beyond | Practical default |
Inter-Process Communication (IPC)
| Method | Description | Characteristics |
|---|---|---|
| Pipe | Unidirectional byte stream between related processes | Kernel buffer; pipe() system call |
| Named Pipe (FIFO) | Like pipe but with a file system name; unrelated processes can connect | Persistent in file system |
| Message Queue | Structured messages with priorities; asynchronous | Kernel persistence |
| Shared Memory | Region of memory mapped into multiple address spaces | Fastest; requires synchronization |
| Socket | Network-transparent endpoint; local (Unix domain) or remote (TCP/UDP) | Most general |
| Signal | Asynchronous notification (e.g., SIGKILL, SIGINT) | Limited data, not queued reliably |
Virtualization
The OS itself can be virtualized:
- Type 1 Hypervisor (bare metal): runs directly on hardware (VMware ESXi, KVM, Xen).
- Type 2 Hypervisor (hosted): runs on a host OS (VirtualBox, VMware Workstation).
Key techniques:
- Trap-and-emulate: guest privileged instructions trap to the hypervisor.
- Binary translation: rewrite non-virtualizable instructions on the fly.
- Hardware-assisted: Intel VT-x / AMD-V provide VMX root/non-root modes.
- Paravirtualization: guest OS is modified to call hypercalls instead of privileged instructions.
References
- Silberschatz, Galvin & Gagne, Operating System Concepts (10th ed.), Wiley.
- Tanenbaum & Bos, Modern Operating Systems (5th ed.), Pearson.
- McKusick, Neville-Neil & Watson, The Design and Implementation of the FreeBSD Operating System (2nd ed.), Addison-Wesley.
- Love, Linux Kernel Development (3rd ed.), Addison-Wesley.
- 2027年计算机操作系统考研复习指导,王道论坛。