The Kingdom of Kernel: Entering the Source
Consider, for a moment, the weight of what surrounds you. Every time you type a command into a terminal, every time you save a document, every time your phone vibrates with a notification—there exists, beneath all of it, a vast kingdom of code. This kingdom operates in silence, managing your files, scheduling your tasks, orchestrating the dance of memory that makes modern computing possible. Its name is the Linux kernel, and for three decades, it has grown from the hobby project of a Finnish student into the foundation of billions of devices worldwide.
You are about to enter this kingdom.
The gates stand open to anyone with curiosity and determination. Millions of lines of C code await behind them, organized with a logic that rewards patient study. But make no mistake: this is not easy terrain. The kernel operates under constraints that no application programmer ever faces. It cannot crash. It cannot pause to ask for your input when something goes wrong. It must run, endlessly, reliably, on hardware it has never seen before, serving applications it cannot predict. Understanding how it accomplishes this Herculean task requires not just reading code, but understanding the philosophy that shaped it.
This chapter marks your initiation. By its end, you will understand the landscape you are navigating. You will know where to find things, how the build system works, and what fundamental principles guided its creation. You will trace the path of a simple system call—read(), that most humble of operations—and discover the layers of indirection, the careful checks, the data structures that stand between your request and the hardware itself. You will meet three of the kernel's most important citizens: the task_struct that represents a running program, the page that represents a chunk of memory, and the dentry that represents a file's presence in the namespace. These will recur throughout our journey together, old friends by the time we reach the final chapter.
Let us begin at the beginning: the repository itself.
The Directory Structure as Sacred Architecture
Open any modern Linux source tree, and you will find something remarkable. The kernel's directory structure is not accidental—it is a carefully designed hierarchy that reflects the kernel's own understanding of itself. Understanding this architecture is the first step toward understanding the code within it.
```mermaid graph TD A[Linux Kernel Source] --> B[arch/] A --> C[block/] A --> D[crypto/] A --> E[drivers/] A --> F[fs/] A --> G[include/] A --> H[init/] A --> I[ipc/] A --> J[kernel/] A --> K[lib/] A --> L[mm/] A --> M[net/] A --> N[samples/] A --> O[scripts/] A --> P[security/] A --> Q[sound/] A --> R[tools/] A --> S[usr/] A --> T[ virt/]
B --> B1[Architecture-specific code<br/>x86, ARM, RISC-V, ...] F --> F1[Filesystem implementations<br/>ext4, XFS, Btrfs, ...] L --> L1[Memory management<br/>vmalloc, slab, mmap, ...] J --> J1[Core kernel code<br/>scheduling, syscalls, time, ...] ```
The root of this tree, often cloned from `git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git`, contains directories that map to the kernel's major subsystems. Let us walk through them as a tourist might walk through a museum, pausing at the most significant galleries.
arch/ stands as the largest directory in most kernels, often consuming more space than several other directories combined. Here resides the architecture-specific code—the implementations that must differ between x86, ARM, RISC-V, MIPS, and dozens of other processor families. When the kernel needs to switch to a different stack, perform a context switch, or handle an interrupt specific to a particular CPU, it consults this directory. Notice, however, how the kernel abstracts these differences. The code you write at a higher level should not care whether you are running on Intel or ARM; the arch/ directory provides the translation layer that makes this portability possible.
fs/ is where filesystems live. This directory contains not just the implementation of the filesystems you might recognize—ext4, XFS, Btrfs, FAT, NTFS—but also the virtual filesystem layer (VFS) that unifies them. The VFS is one of the kernel's great achievements: a common interface that allows any process to access files through the same system calls, regardless of what filesystem actually stores those files. When we discuss dentry later in this chapter, we will be discussing something that lives at this layer.
mm/ contains the memory management subsystem. The vmalloc subsystem, the page allocator, the slab allocator for kernel objects, the code that handles mmap and munmap—all of it resides here. This is territory we will visit extensively in Chapter 5, when we explore the kingdom of memory in depth.
kernel/ holds the core of the kernel proper: the scheduler, the implementation of system calls, the code that manages processes and threads, the timer subsystem, and the power management code. When you read about the "kernel" in casual conversation, this is often what people mean—the central core that everything else builds upon.
drivers/ consumes the most space in most kernels, often comprising 60% or more of the total codebase. This makes sense: the Linux kernel runs on everything from supercomputers to wristwatches, and each device requires its own driver. The drivers/ directory is organized by class (usb/, pci/, i2c/, gpu/, network/, etc.) and then by specific device or chip. Writing drivers is often the first way new contributors engage with the kernel; the subsystem has well-established APIs and extensive documentation.
net/ and block/ handle networking and block storage respectively. The block layer is particularly interesting because it sits at the intersection of filesystems (which want to read and write files) and devices (which provide the actual storage). We will explore this boundary when we discuss how read() actually works.
crypto/, security/, lib/, init/, ipc/, sound/, and virt/ round out the major subsystems. Each serves a distinct purpose. The crypto/ directory provides cryptographic primitives used throughout the kernel and available to drivers. The security/ directory contains security modules like SELinux and AppArmor. The init/ directory contains the startup code—the first C code that runs after the architecture-specific boot. The ipc/ directory implements inter-process communication primitives: semaphores, shared memory, and message queues. The virt/ directory handles virtualization, containing both the KVM hypervisor and the common virtualization infrastructure layer.
Notice what the directory structure does not contain: there is no clear separation between "user-facing" and "internal" code. The kernel is monolithic in the truest sense. Every subsystem can call every other subsystem. This is a deliberate choice, made for performance. In a microkernel design, these subsystems would be separate processes communicating through message passing. The Linux kernel chose a different path: everything in one address space, direct function calls between subsystems, maximum performance at the cost of a single failure domain. If any part of the kernel crashes, they all crash. This is the bargain Linux struck, and it has proven remarkably successful.
The include/ directory deserves special mention. It sits at the top level because it is included everywhere: it contains the header files that define the kernel's public interfaces. The include/linux/ subdirectory contains headers used throughout the kernel, while include/uapi/ contains the headers meant for user-space programs. The separation is important: uapi headers define the stable ABI that userspace programs rely upon. Changes to these headers must be made carefully, because they affect every program that compiles against them.