Operating Systems: CPU Cache Locality
Measured on an M1: column-order matrix walks up to 30x slower, shuffled lists at 97 ns a node, and false sharing slowing two counters 4x.
What happens under your code: processes and threads, scheduling, virtual memory and page faults, locks and deadlocks, file descriptors and the page cache.
Official reference: Operating Systems: Three Easy Pieces
Measured on an M1: column-order matrix walks up to 30x slower, shuffled lists at 97 ns a node, and false sharing slowing two counters 4x.
How fork shares pages until a write, measured on macOS: 16,517 pages copied on write, few on read, and Python copying pages just by reading.
Why select stops at 1,024 descriptors and poll slows with every idle socket, how epoll and kqueue avoid both, and when edge-triggered mode bites.
Blocking, non-blocking, multiplexed and completion I/O compared: a 1-second spin burned a full second of CPU, and 2,000 threads cost 70 MiB.
Why a crash mid-update corrupts a filesystem, how write-ahead journaling and replay fix it, ext4's modes, and what journaling won't do for you.
Why a condition variable wait sits in a while loop under the lock, with Python runs of a stolen wakeup, a lost notify and a notify() deadlock.
What a context switch saves and loses, voluntary vs involuntary switches, a 3.5 µs pipe ping-pong on an M1, and the real cost of 512 threads.
FIFO, SJF, STCF and round robin simulated with real numbers, the turnaround vs response trade-off, and what an M1's efficiency cores add.
Reproduce a two-account deadlock in Python, map it to the four Coffman conditions, and fix it with lock ordering, timeouts or a different design.
What a file descriptor points to, which limits apply, and a real EMFILE reproduced in a throwaway process, plus how to find the leak with lsof.
What write, fsync and F_FULLFSYNC really promise, measured on an M1 (2 µs, 25 µs, 3.8 ms), plus atomic file replacement and group commit.
From a NIC interrupt to the thread that reads the data: top halves, deferred work, and a polling vs blocking test showing the CPU-latency trade.
Pipes, Unix sockets, TCP loopback and shared memory compared on a 64 MiB transfer, plus the synchronization and trust costs each one carries.
A mutex has an owner, a semaphore counts permits. Real Python and pthread runs show why that matters for releasing, over-release and debugging.
Random 4 KiB reads took 189 µs cold and 1.6 µs warm on an M1. How the page cache works, how to benchmark around it, and when to bypass it.
Minor, major and invalid page faults, with real counts: 4,096 faults for 64 MiB on an M1, and a cold mmap read 34 times slower than warm.
How a medium-priority task blocks an urgent one via a lock it never touches, simulated tick by tick, plus priority inheritance and Pathfinder.
What a process owns, what its threads share, and what that costs: macOS runs of thread vs fork vs spawn, and a segfault that kills a process.
Unbounded, blocking and dropping queues measured in Python: how a bound turns overload into a policy, how to size it, and how to shut it down.
Lost updates and check-then-act races, run in Node and Python: why a racy counter can pass tests, and how atomics, locks and O_EXCL fix it.
Stack and heap as lifetime decisions: real addresses, stack overflows at 7,600 vs 400 frames on macOS, and a dangling pointer clang flags.
A reader-preference lock starved a writer for 1.9 s in Python. Where starvation comes from, what fairness costs, and how schedulers bound it.
How the CPU stops your code doing privileged work, what a system call costs, and why clock_gettime skips the kernel, with real M1 measurements.
Why two processes can hold different values at the same address, how page tables translate it, and why a 32 GiB mapping can cost almost nothing.
Why throughput collapses when active memory exceeds RAM: in an LRU test, one frame fewer took faults from 2% to 100%. How to spot and fix it.
A process is an execution environment with its own virtual address space and resources. Threads within a process share that address space but have their own stacks and execution state. Sharing memory makes communication convenient and introduces synchronization risks. Processes generally provide stronger isolation, while threads can be useful for concurrent work inside an application. The costs of creation and scheduling depend on the operating system and runtime.
Likely follow-up: When would you choose worker processes over threads?
Deadlock occurs when participants wait indefinitely for resources held by one another. The classic conditions are mutual exclusion, holding resources while waiting, no forced preemption and circular wait. A consistent global lock acquisition order can break circular wait. Other approaches include reducing lock scope or avoiding holding one lock while acquiring another. Timeouts help detect or escape a wait, but recovery must release resources and preserve invariants.
Likely follow-up: How is deadlock different from starvation?
Virtual memory gives processes an address space that is mapped to physical memory through page tables. It supports isolation, protection and flexible allocation without requiring contiguous physical memory. A page fault means a mapping needs attention; it does not always mean disk I/O, because demand-zero allocation or copy-on-write can also fault. Heavy paging can make a system slow, so I would inspect memory pressure and the working set before simply adding threads.
Likely follow-up: What happens when a process repeatedly accesses more memory than RAM can hold?
Concurrency means multiple tasks can make progress over overlapping periods; parallelism means tasks execute at the same time. A single CPU core can support concurrency by switching between tasks. Multiple cores can enable parallel execution, subject to runtime constraints and workload structure. I would use concurrency to overlap waiting and parallelism to accelerate divisible computation. Both require care with shared state, and overhead can outweigh the benefit for small tasks.
Likely follow-up: Can an application be concurrent without being parallel?
Measure memory over a repeatable workload and distinguish a temporary peak or cache warmup from sustained growth. Compare heap snapshots or allocation profiles to find objects that remain reachable unexpectedly. Also inspect native allocations, open resources and memory mappings because heap metrics may not explain the full resident size. Look for unbounded caches, retained listeners and queues. Confirm a fix by repeating the same workload and observing a stable memory trend.
Likely follow-up: Why can memory leak in a garbage-collected language?
Short time slices can improve responsiveness by giving runnable tasks frequent opportunities to execute, but context switching adds overhead and can disturb caches. Long slices may improve efficiency for sustained work while making interactive tasks wait longer. The actual policy also considers priorities and fairness. I would evaluate response latency and throughput for the target workload rather than assume one time slice works everywhere. Tasks waiting for I/O need not consume their full slice.
Likely follow-up: How would an interactive workload differ from a batch workload?
A condition variable is used with shared state protected by a mutex. Waiting atomically releases the mutex and blocks, then reacquires it before returning. A wakeup does not guarantee the desired predicate is true: another thread may have consumed the resource, and some APIs permit spurious wakeups. Check the predicate again while holding the mutex and wait if necessary. The shared predicate, rather than the notification itself, determines whether proceeding is safe.
Likely follow-up: How can checking the predicate outside the lock lose a notification?
Files and sockets hold operating-system resources that are finite, even when the language manages object memory automatically. Relying on garbage collection can delay cleanup and exhaust descriptors or connections. Use the language’s structured cleanup mechanism so normal returns and exceptions both release the resource. Make ownership clear when a resource is shared. I would monitor descriptor counts and connection pools when a service fails only after running for a long time.
Likely follow-up: What symptoms would you expect from a file-descriptor leak?
No questions match that filter.
Prefer multiple choice? All 11 Operating Systems MCQs with answers →