The Page Cache and Page Writeback

The Linux Page Cache & Page Writeback The Core Concept: Disk access is measured in milliseconds; RAM access is measured in nanoseconds. To bridge this massive performance gap, Linux dynamically uses free physical RAM to cache blocks of disk data. This relies on temporal locality: the computing principle that if data is accessed once, it is highly likely to be accessed again very soon. 1. Reading from Disk The cache is granular; Linux caches specific pages of files based on what you actually access, not whole files by default. ...

July 5, 2026 · 25 min

The Page Tables

Modern Linux Page Tables (4-Level Architecture) Modern 64-bit processors require much larger address spaces, and the Linux kernel adapted by shifting to a 4-level (and more recently, a 5-level) page table architecture. The Core Concept: Because a 64-bit address space is astronomically large and mostly empty, the kernel cannot use a single, massive translation array. Instead, it uses a hierarchical, multi-level tree of tables to map Virtual Addresses to Physical Addresses efficiently. ...

July 2, 2026 · 18 min

The Process Address Space

The Process Address Space — Intro Same flat (single contiguous range) address space model you already have from the article — nothing new conceptually. Key term to lock in: a memory area (this book’s name for what the article calls a VMA) is a permission-tagged interval within that address space. Access outside any valid area, or against an area’s permissions (write to read-only, execute non-executable) → segfault. The list of “what memory areas contain” is just a slightly different cut of the same segments from the article: text, data, bss, stack, shared library mappings, mmap’d files, shared memory, anonymous mappings (malloc). All non-overlapping — every valid address belongs to exactly one area. ...

June 26, 2026 · 32 min

The Virtual Filesystem

The Virtual Filesystem What is a Filesystem? Imagine a bare hard drive as a massive, empty warehouse. You can throw billions of bytes of data in there, but without a system, you will never find anything again. A filesystem is the specific set of rules, data structures, and “ledgers” (like the Inodes and Superblocks we discussed) used to organize, index, and retrieve that data. It dictates how large a file can be, how folders are nested, and how permissions are handled. ...

June 20, 2026 · 27 min

Memory Management in Kernel

Memory Management in Kernel Why Kernel Space Memory Management is Harder? Userspace can fail safely and wait patiently. Kernel-space cannot, making its allocation fundamentally harder. Key Differences in Kernel Memory Allocation Sleeping is often banned: Userspace can block while waiting for memory. Kernel contexts (interrupts, spinlocks) cannot, requiring instant success or failure flags like GFP_ATOMIC. Failure is catastrophic: App failures just kill the app; kernel allocation failures crash the entire system. The kernel must rely on emergency reserves, reclaim, and the OOM killer to survive. Dangerous recursion: The kernel manages memory using memory. A reclaim operation can trigger filesystem actions that need more memory, causing deadlocks (prevented by flags like GFP_NOFS). Strict physical constraints: Userspace only worries about virtual memory. The kernel must manage physical pages, DMA limits, and NUMA locality. Physical fragmentation matters: The kernel frequently requires physically contiguous pages, making fragmentation a critical, system-halting roadblock. Interrupt context is brutal: Interrupt handlers need immediate memory without sleeping or waiting on locks, relying heavily on per-CPU caches and lockless structures. High deadlock risk: Allocators interact with reclaim, writebacks, and system locks. GFP flags are essential to dictate exactly what an allocator is safely allowed to do. Predictable latency is required: Kernel subsystems (networking, real-time workloads) cannot tolerate unpredictable allocation pauses, necessitating highly optimized allocators like SLAB/SLUB. The Mental Shift ...

May 19, 2026 · 31 min

Rust Notes — Module 6

Rust Notes — Module 6: Error Handling 1. The Philosophy Language Error Mechanism Problem C Return codes (-1, NULL, errno) Easy to ignore, no enforcement Go (value, error) tuples Better, but still ignorable Rust Result<T, E> in the type system Impossible to ignore — compiler enforced If a function can fail, its return type says so. You cannot use the success value without handling the error case first. No hidden exceptions, no surprise crashes from ignored error codes. ...

March 29, 2026 · 7 min

Rust Notes — Module 7

Rust Notes — Module 7: Packages, Crates & Modules 1. How Rust Compilation Works In C, you hand the compiler a list of files. Each file compiles independently, and a linker stitches the object files together. The problem — the compiler has no idea which files depend on which, so you need a Makefile to track what needs recompiling when something changes. Rust takes a different approach. You hand the compiler one root file (main.rs or lib.rs). The compiler follows mod declarations to find every other file that’s part of the crate. Because Rust knows the full dependency graph from the start, it handles incremental recompilation internally — no Makefile needed. ...

March 29, 2026 · 8 min

Rust Notes — Module 5

Rust Notes — Module 5: Generics, Closures & Iterators 1. Generics Generics let you write code that works for any type, with the compiler generating specialized versions at compile time. Zero runtime overhead — same as C++ templates. Generic Functions // T is a type parameter — placeholder for any concrete type fn largest<T: PartialOrd>(list: &[T]) -> &T { let mut largest = &list[0]; for item in list { if item > largest { largest = item; } } largest } // works for any type that implements PartialOrd let nums = vec![1, 5, 3, 2]; let chars = vec!['a', 'z', 'm']; println!("{}", largest(&nums)); // 5 println!("{}", largest(&chars)); // z Generic Structs struct Stack<T> { elements: Vec<T>, } // impl block also needs <T> impl<T> Stack<T> { fn new() -> Self { Stack { elements: Vec::new() } } fn push(&mut self, item: T) { self.elements.push(item); } fn pop(&mut self) -> Option<T> { self.elements.pop() } fn peek(&self) -> Option<&T> { self.elements.last() } fn is_empty(&self) -> bool { self.elements.is_empty() } } // type parameter inferred from usage let mut int_stack = Stack::new(); int_stack.push(1); int_stack.push(2); // or explicit let mut move_stack: Stack<Move> = Stack::new(); Generic Enums You’ve already used these — Option<T> and Result<T, E> are generic enums: ...

March 22, 2026 · 10 min

Rust Notes — Module 3

Rust Notes — Module 3: Structs & Enums 1. Structs Structs group related data together into a named type — same concept as C structs, cleaner syntax. struct User { username: String, email: String, age: u32, active: bool, } Creating and Accessing let user = User { username: String::from("sanketh"), email: String::from("sanketh@example.com"), age: 20, active: true, }; println!("{}", user.username); // field access with . Mutability The entire instance must be mut — you cannot mark individual fields as mutable: ...

March 21, 2026 · 7 min

Rust Notes — Module 4

Rust Notes — Module 4: Traits 1. What is a Trait? A trait defines a set of behaviors (methods) that a type must implement. It is a contract — any type that implements the trait promises to provide those behaviors. trait Greet { fn hello(&self) -> String; } Concept Rust C++ Go Trait trait abstract class / concept interface Implementation impl Trait for Type override virtual method implicit (duck typing) Dispatch static (default) or dynamic virtual table interface table 2. Implementing a Trait struct Human { name: String } struct Robot { id: u32 } impl Greet for Human { fn hello(&self) -> String { format!("Hi, I'm {}!", self.name) } } impl Greet for Robot { fn hello(&self) -> String { format!("BEEP. I AM UNIT {}.", self.id) } } Each type provides its own implementation of the trait methods. A type can implement any number of traits. You can implement traits for types you didn’t define (with some restrictions — see orphan rule below). 3. Default Implementations Traits can provide default method implementations. Types can override them or inherit them for free: ...

March 21, 2026 · 8 min