Loading content...
The Linux kernel is one of the largest collaborative software projects in human history. As of 2024, it comprises over 30 million lines of code, contributed by tens of thousands of developers across three decades. Yet despite this scale, the source tree is remarkably well-organized—a testament to disciplined engineering and Linus Torvalds' insistence on maintainability.
For anyone seeking to understand Linux internals, contribute patches, write drivers, or debug production issues, understanding the source layout is essential. This page provides a comprehensive map of the kernel source tree, explaining not just where things are, but why they're organized that way.
By the end of this page, you will be able to: (1) Navigate the kernel source tree confidently, (2) Locate specific subsystems quickly, (3) Understand the purpose of each top-level directory, (4) Use kernel source navigation tools effectively, and (5) Read and understand the supporting infrastructure files.
When you extract or clone the Linux kernel source (git clone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git), you'll find approximately 20 top-level directories. Each has a specific purpose and contains related functionality.
Obtaining the Source:
# Clone the official repository (full history, ~4GB)
git clone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
# Or download a specific release tarball
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.6.tar.xz
tar -xf linux-6.6.tar.xz
Let's examine what you'll find:
| Directory | Purpose | Approximate Size | Key Contents |
|---|---|---|---|
arch/ | Architecture-specific code | ~150MB (12%) | x86, arm64, riscv boot code, assembly, platform support |
block/ | Block device subsystem | ~1.5MB | Block I/O scheduling, partition handling |
certs/ | Kernel module signing certificates | ~20KB | Certificate handling, key management |
crypto/ | Cryptographic algorithms | ~6MB | AES, SHA, RSA, ECDSA implementations |
Documentation/ | Official kernel docs | ~80MB | API docs, subsystem guides, admin guides |
drivers/ | Device drivers | ~700MB (55%) | GPU, network, storage, USB, input drivers |
fs/ | File systems | ~80MB | ext4, xfs, btrfs, nfs, vfs layer |
include/ | Header files | ~50MB | Kernel APIs, uapi for userspace |
init/ | Kernel initialization | ~500KB | start_kernel(), initramfs handling |
ipc/ | Inter-process communication | ~200KB | SysV IPC, POSIX mqueue |
kernel/ | Core kernel code | ~15MB | Scheduler, signals, time, tracing |
lib/ | Helper libraries | ~8MB | String functions, compression, radix trees |
mm/ | Memory management | ~6MB | Page allocator, slab, vmalloc, mmap |
net/ | Networking stack | ~80MB | TCP/IP, sockets, netfilter, Bluetooth |
samples/ | Example code | ~2MB | Sample BPF, kprobes, tracers |
scripts/ | Build/development scripts | ~5MB | Makefiles, config tools, checkpatch |
security/ | Security frameworks | ~4MB | SELinux, AppArmor, TOMOYO, LSM |
sound/ | Audio subsystem | ~35MB | ALSA, SoC audio, drivers |
tools/ | Userspace tools | ~30MB | perf, bpftool, selftests |
usr/ | Initramfs generation | ~100KB | Early userspace creation |
virt/ | Virtualization support | ~1MB | KVM core (arch-specific in arch/) |
Notice that drivers/ constitutes over 55% of the kernel source. This reflects Linux's extraordinary hardware support—thousands of device drivers for everything from ancient ISA cards to cutting-edge AI accelerators. The core kernel (scheduler, memory manager, VFS) is actually quite compact relative to driver code.
The arch/ directory contains code specific to each processor architecture Linux supports. This is where the kernel interfaces with the bare metal—boot sequences, interrupt handling, context switching, memory management unit configuration, and system call entry points.
Supported Architectures (as of Linux 6.x):
| Architecture | Directory | Examples | Market Segment |
|---|---|---|---|
| x86 (32/64-bit) | arch/x86/ | Intel, AMD processors | Desktops, servers, laptops |
| ARM 64-bit | arch/arm64/ | Apple M1/M2, Graviton, Snapdragon | Phones, tablets, ARM servers |
| ARM 32-bit | arch/arm/ | Raspberry Pi, older SoCs | Embedded, IoT |
| RISC-V | arch/riscv/ | SiFive, emerging SoCs | Embedded, education, emerging servers |
| PowerPC | arch/powerpc/ | IBM POWER servers | Enterprise, mainframes |
| s390 | arch/s390/ | IBM zSeries mainframes | Enterprise mainframes |
| MIPS | arch/mips/ | Loongson, older routers | Legacy embedded, networking |
| LoongArch | arch/loongarch/ | Loongson 3A5000+ | Chinese domestic processors |
Structure of arch/x86/ (the most common architecture):
arch/x86/
├── boot/ # Bootloader interface, compressed kernel setup
│ ├── compressed/ # Decompression code for vmlinuz
│ └── tools/ # Build tools for boot image
├── entry/ # System call & interrupt entry points
│ ├── entry_64.S # 64-bit syscall entry (assembly)
│ └── common.c # Common entry code
├── include/ # x86-specific headers
│ ├── asm/ # Architecture-specific asm/ headers
│ └── uapi/ # Userspace-visible definitions
├── kernel/ # x86 core kernel
│ ├── cpu/ # CPU feature detection, microcode
│ ├── process.c # Context switching
│ ├── signal.c # Signal handling
│ └── setup.c # Early boot setup
├── mm/ # x86 memory management
│ ├── pgtable.c # Page table manipulation
│ ├── fault.c # Page fault handling
│ └── tlb.c # TLB flush operations
├── kvm/ # KVM virtualization (x86-specific)
├── platform/ # Platform-specific (Chrome, Surface, etc.)
└── Makefile # Build rules for x86
Generic kernel code includes <asm/something.h> (e.g., <asm/page.h>). During build, include/asm is a symlink to arch/$(ARCH)/include/asm. This allows architecture-independent code to use architecture-specific definitions transparently—a classic abstraction pattern.
Key Files to Understand:
arch/x86/entry/entry_64.S: The assembly code for 64-bit system call entry. Every system call passes through here. Understanding this file reveals how user-space transitions to kernel-space.
arch/x86/kernel/process_64.c: Contains __switch_to(), the context switch implementation. Shows how the kernel saves and restores CPU state.
arch/x86/mm/fault.c: Page fault handler for x86. Called when a memory access triggers a fault—key for understanding demand paging and copy-on-write.
arch/x86/boot/header.S: The very first code executed when the kernel boots (after the bootloader). Sets up protected mode and jumps to C code.
The kernel/ directory contains architecture-independent core kernel functionality—the heart of what makes Linux an operating system. This is where scheduling, process management, signals, time management, and tracing live.
Key Subdirectories and Files:
kernel/├── sched/ # CPU Scheduler│ ├── core.c # Scheduler core (schedule(), context switch trigger)│ ├── fair.c # CFS - Completely Fair Scheduler│ ├── rt.c # Real-time scheduler (SCHED_FIFO, SCHED_RR)│ ├── deadline.c # Deadline scheduler (SCHED_DEADLINE)│ ├── idle.c # Idle task scheduling│ └── topology.c # NUMA/SMP topology awareness│├── locking/ # Locking primitives│ ├── mutex.c # Mutex implementation│ ├── spinlock.c # Spinlock implementation│ ├── rwsem.c # Read-write semaphores│ └── lockdep.c # Lock dependency validation (debugging)│├── time/ # Time subsystem│ ├── time.c # Basic time functions│ ├── timekeeping.c # Wall clock, monotonic time│ ├── timer.c # Kernel timers│ └── hrtimer.c # High-resolution timers│├── trace/ # Tracing infrastructure│ ├── ftrace.c # Function tracer│ ├── trace_events.c # Tracepoint events│ └── trace_kprobe.c # Kernel probes│├── bpf/ # eBPF subsystem│ ├── core.c # BPF virtual machine│ ├── verifier.c # BPF program verifier (safety checks)│ └── syscall.c # BPF system calls│├── cgroup/ # Control groups│ ├── cgroup.c # Core cgroup functionality│ └── cpuset.c # CPU set cgroup controller│├── fork.c # Process creation (do_fork, copy_process)├── exit.c # Process termination├── signal.c # Signal handling├── sys.c # System calls (getpid, uname, etc.)├── printk/ # Kernel logging (printk)├── panic.c # Kernel panic handling├── module.c # Module loader core├── workqueue.c # Kernel workqueues├── kthread.c # Kernel threads└── power/ # Power management (suspend, hibernate)Deep Dive: The Scheduler (kernel/sched/)
The scheduler is arguably the most critical subsystem. Let's examine its organization:
core.c (~8,000 lines): The heart of scheduling. Contains schedule(), which is called whenever the kernel needs to pick the next task to run. Also contains context_switch(), load balancing, and CPU hotplug handling.
fair.c (~12,000 lines): The Completely Fair Scheduler (CFS), Linux's default. Implements a red-black tree of runnable processes ordered by virtual runtime, ensuring fairness over time.
rt.c (~2,500 lines): Real-time scheduling classes SCHED_FIFO (first-in-first-out, runs until it yields) and SCHED_RR (round-robin with time slices).
deadline.c (~2,500 lines): SCHED_DEADLINE, an Earliest Deadline First scheduler for hard real-time tasks.
idle.c: The idle task—what the CPU runs when there's nothing else to do. Manages power-saving states (C-states).
To understand how scheduling works, trace these functions: schedule() → pick_next_task() → pick_next_task_fair() → selection from the CFS runqueue → context_switch() → switch_to() (arch-specific). Reading these in order provides a complete picture of how Linux switches between processes.
The mm/ directory contains Linux's sophisticated memory management subsystem—one of the most complex and performance-critical parts of the kernel. It handles everything from allocating single pages to managing terabytes of RAM with NUMA awareness.
Core Components:
| File | Purpose | Key Functions |
|---|---|---|
page_alloc.c | Physical page allocator (buddy system) | alloc_pages(), free_pages(), zone management |
slab.c/slub.c | Slab allocator for kernel objects | kmalloc(), kfree(), cache management |
vmalloc.c | Virtual contiguous allocation | vmalloc(), vfree() for large allocations |
mmap.c | Memory mapping | do_mmap(), VMA management, file mapping |
memory.c | Page fault handling | handle_mm_fault(), page table walking |
filemap.c | Page cache | File-backed page management, readahead |
swap.c | Swap management | Page swap-out, swap-in operations |
vmscan.c | Page reclamation (kswapd) | LRU lists, page aging, memory pressure |
oom_kill.c | OOM killer | Process killing when memory exhausted |
mmu_notifier.c | MMU change notifications | For virtualization, RDMA devices |
hugetlb.c | Huge pages (2MB, 1GB) | Huge page allocation and management |
numa.c | NUMA policy | Memory placement for NUMA systems |
Memory Allocation Layers:
Linux has multiple allocation layers, each serving different needs:
User Space
↓
┌───────────────────────────────┐
│ malloc()/mmap() │ ← C library
└───────────────┬───────────────┘
↓ System Call
┌───────────────────────────────┐
│ sys_brk() / sys_mmap() │ ← VMA layer (mm/mmap.c)
│ Virtual Memory Areas │
└───────────────┬───────────────┘
↓ Page Fault
┌───────────────────────────────┐
│ handle_mm_fault() │ ← Page table management
│ Page Table Manipulation │ (mm/memory.c)
└───────────────┬───────────────┘
↓
┌───────────────────────────────┐
│ Slab Allocator (SLUB) │ ← Object caching
│ kmalloc(), kmem_cache_* │ (mm/slub.c)
└───────────────┬───────────────┘
↓
┌───────────────────────────────┐
│ Buddy Allocator │ ← Physical pages
│ alloc_pages(), free_pages │ (mm/page_alloc.c)
└───────────────┬───────────────┘
↓
┌───────────────────────────────┐
│ Physical Memory (RAM) │
└───────────────────────────────┘
Linux has three slab allocator implementations: SLAB (traditional, feature-rich, deprecated), SLUB (current default, simpler, better debugging), and SLOB (minimalist for embedded). Most systems use SLUB. The allocator is selected at kernel configuration time.
The fs/ directory contains the Virtual File System (VFS) layer and implementations of numerous file systems. VFS provides the unified interface that allows operations like open(), read(), and write() to work consistently across ext4, XFS, NFS, and dozens of other file systems.
VFS Core Files:
fs/├── Super Block & Mounting│ ├── super.c # Superblock operations│ ├── namespace.c # Mount namespaces│ └── mount.h # Mount structures│├── VFS Core│ ├── inode.c # Inode management│ ├── file.c # File operations│ ├── dcache.c # Directory entry cache│ ├── namei.c # Path name resolution│ ├── open.c # sys_open() implementation│ ├── read_write.c # sys_read()/sys_write()│ └── stat.c # File status operations│├── Buffer/Page Cache│ ├── buffer.c # Block buffer management│ └── direct-io.c # O_DIRECT I/O path│├── File System Implementations│ ├── ext4/ # ext4 file system (~60K lines)│ ├── xfs/ # XFS file system (~100K lines)│ ├── btrfs/ # Btrfs (~130K lines)│ ├── f2fs/ # Flash-Friendly FS│ ├── nfs/ # Network File System client│ ├── cifs/ # SMB/CIFS client│ ├── fuse/ # Filesystem in Userspace│ ├── overlayfs/ # Overlay file system (containers)│ ├── proc/ # /proc pseudo-filesystem│ ├── sysfs/ # /sys pseudo-filesystem│ ├── devtmpfs/ # /dev device nodes│ └── tmpfs/ # In-memory filesystem│└── Misc ├── quota/ # Disk quota support ├── notify/ # inotify, fsnotify └── locks.c # File locking (flock, fcntl)VFS Object Model:
VFS defines four fundamental object types that file systems must implement:
Superblock (struct super_block): Represents a mounted file system. Contains file system metadata like block size, max file size, and mount options. Each mount has one superblock.
Inode (struct inode): Represents a file on disk. Contains metadata (permissions, timestamps, size) and operation pointers. Inodes are cached in memory for performance.
Dentry (struct dentry): Represents a directory entry—a (name, inode) pair. Dentries form the directory cache (dcache), dramatically speeding path lookups.
File (struct file): Represents an open file descriptor. Contains the current position, open flags, and pointers to operations. Multiple file structs can point to the same inode.
Operation Vectors:
Each object type has an associated operations structure (function pointer table):
struct super_operations — Superblock operations (sync, inode allocation)struct inode_operations — Inode operations (create, lookup, link)struct file_operations — File operations (read, write, mmap, ioctl)struct dentry_operations — Dentry operations (compare, delete)To trace how read() works: sys_read() (fs/read_write.c) → vfs_read() → file->f_op->read() or ->read_iter() → file system's read function → page cache lookup (mm/filemap.c) → if miss, disk I/O (block layer). Understanding this chain is essential for performance debugging.
The drivers/ directory is by far the largest in the kernel, comprising over 55% of all source code. This reflects Linux's extraordinary hardware support—if a device exists, there's probably a Linux driver for it.
Organization by Device Class:
| Directory | Contents | Size | Examples |
|---|---|---|---|
drivers/gpu/ | Graphics drivers | ~120MB | DRM core, i915, amdgpu, nouveau |
drivers/net/ | Network drivers | ~100MB | Ethernet, WiFi, Bluetooth drivers |
drivers/scsi/ | SCSI/storage | ~25MB | SCSI core, HBA drivers |
drivers/nvme/ | NVMe SSDs | ~1MB | NVMe host driver |
drivers/usb/ | USB stack | ~15MB | USB core, host controllers, gadgets |
drivers/pci/ | PCI bus | ~2MB | PCI enumeration, hot-plug |
drivers/block/ | Block devices | ~3MB | loop, virtio-blk, NVDIMMs |
drivers/char/ | Character devices | ~5MB | TTY, random, mem devices |
drivers/input/ | Input devices | ~6MB | Keyboard, mouse, touchscreen |
drivers/i2c/ | I2C bus | ~4MB | I2C controllers and devices |
drivers/spi/ | SPI bus | ~2MB | SPI controllers and devices |
drivers/acpi/ | ACPI power mgmt | ~2MB | ACPI tables, power states |
drivers/platform/ | Platform drivers | ~10MB | Vendor-specific (Dell, Lenovo) |
drivers/vfio/ | Device passthrough | ~500KB | VFIO for VM device access |
Driver Model:
Linux has a unified driver model centered on buses, devices, and drivers:
// drivers/base/bus.c, driver.c, device.c
struct bus_type {
const char *name; // "pci", "usb", "platform"
int (*match)(struct device *, struct device_driver *);
int (*probe)(struct device *);
// ...
};
struct device_driver {
const char *name;
struct bus_type *bus;
int (*probe)(struct device *);
void (*remove)(struct device *);
// ...
};
struct device {
struct device *parent;
struct bus_type *bus;
struct device_driver *driver;
// ...
};
Registration Flow:
bus_register())driver_register())bus->match())driver->probe())The drivers/gpu/ directory alone is larger than the entire core kernel! Modern GPUs are astonishingly complex, and drivers must handle 3D rendering, video decode, display pipelines, power management, and memory management. The AMD GPU driver (amdgpu) exceeds 2 million lines of code.
The net/ directory contains Linux's complete networking stack—one of the most mature and performant in existence. It handles everything from TCP/IP to Bluetooth to packet filtering.
Major Subsystems:
net/├── Core Networking│ ├── core/ # Socket layer, SKB management│ │ ├── sock.c # Generic socket operations│ │ ├── skbuff.c # Socket buffer (SKB) management│ │ └── dev.c # Network device core│ └── socket.c # Socket system calls│├── Layer 3 (Network)│ ├── ipv4/ # IPv4 implementation│ │ ├── tcp.c # TCP core│ │ ├── tcp_input.c # TCP receive path│ │ ├── tcp_output.c # TCP send path│ │ ├── udp.c # UDP implementation│ │ ├── ip_input.c # IP receive│ │ ├── ip_output.c # IP send│ │ └── route.c # IP routing│ ├── ipv6/ # IPv6 implementation│ └── unix/ # Unix domain sockets│├── Packet Filtering/Firewall│ ├── netfilter/ # Netfilter framework│ │ ├── nf_tables/ # nftables (modern)│ │ ├── ipset/ # IP set support│ │ └── xt_*.c # iptables match/target modules│ ├── bpf/ # BPF for networking│ └── xdp/ # eXpress Data Path│├── Traffic Control│ └── sched/ # Packet scheduling (QoS)│ ├── sch_htb.c # Hierarchical Token Bucket│ ├── sch_fq.c # Fair Queueing│ └── sch_tbf.c # Token Bucket Filter│├── Wireless│ ├── wireless/ # cfg80211 (WiFi configuration)│ └── mac80211/ # 802.11 MAC layer│├── Other Protocols│ ├── sctp/ # SCTP protocol│ ├── dccp/ # DCCP protocol│ ├── rds/ # Reliable Datagram Sockets│ ├── tipc/ # TIPC cluster protocol│ └── bluetooth/ # Bluetooth stack│└── Container Networking ├── bridge/ # Network bridging └── ipvlan/ # IP VLANThe Socket Buffer (SKB):
The fundamental data structure for networking is struct sk_buff, defined in include/linux/skbuff.h. Every network packet—incoming or outgoing—is wrapped in an SKB:
struct sk_buff {
struct sk_buff *next, *prev; // Queue links
struct sock *sk; // Owning socket
struct net_device *dev; // Associated device
unsigned char *head; // Buffer start
unsigned char *data; // Payload start (moves through layers)
unsigned char *tail; // Payload end
unsigned char *end; // Buffer end
unsigned int len; // Packet length
unsigned int data_len; // Data fragments length
__u16 protocol; // Packet protocol
__u16 transport_header; // Offset to L4 header
__u16 network_header; // Offset to L3 header
__u16 mac_header; // Offset to L2 header
// ... many more fields
};
As packets traverse the network stack, the data pointer is adjusted to point to the current layer's header—a clever design that avoids copying.
For TCP/IP performance tuning, key files to study are: tcp_input.c (receive-side behavior, window scaling), tcp_output.c (send-side, congestion control), and tcp_congestion.c (pluggable congestion control algorithms like BBR, CUBIC). Understanding these enables intelligent tuning via /proc/sys/net/ipv4/.
Several directories support the kernel build process and development workflow:
scripts/ - Build Tools and Development Utilities:
checkpatch.pl — The coding style checker. All patches must pass before submission. Run ./scripts/checkpatch.pl --file path/to/file.c to check style compliance.get_maintainer.pl — Finds who maintains a given file and should review patches. Essential for submitting patches to the correct mailing lists.coccinelle/ — Semantic patches for large-scale code transformations. Used for API changes across thousands of files.kconfig/ — The Kconfig configuration system (menuconfig, xconfig, etc.).Makefile.* — Build system infrastructure files.sign-file — Module signing utility for Secure Boot compliance.Kconfig Files:
Every directory contains a Kconfig file defining configuration options for that subsystem:
# Example from drivers/net/Kconfig
menuconfig NET_ETHERNET
bool "Ethernet device support"
depends on NET
help
Enable support for Ethernet devices.
config E1000E
tristate "Intel(R) PRO/1000 PCI-Express Gigabit"
depends on PCI && NET_ETHERNET
help
This driver supports Intel PRO/1000 adapters.
To compile as a module, choose M. The module
will be called e1000e.
Kconfig defines the options you see in make menuconfig. Note tristate—can be Y (built-in), M (module), or N (not compiled).
Makefile Structure:
Kernel Makefiles use a specialized syntax. Example from drivers/net/ethernet/intel/e1000e/:
# Conditional compilation based on config
obj-$(CONFIG_E1000E) += e1000e.o
# Multi-file module
e1000e-objs := netdev.o ethtool.o param.o hw.o
# Compiler flags specific to this directory
ccflags-y += -I$(srctree)/drivers/net/ethernet/intel
The obj-$(CONFIG_...) pattern ensures files are only compiled if configured.
The Documentation/ directory contains extensive docs in reStructuredText format, buildable to HTML via make htmldocs. Key subdirectories: Documentation/admin-guide/ (sysadmin topics), Documentation/driver-api/ (driver development), Documentation/core-api/ (kernel APIs), and Documentation/process/ (development process).
With 30+ million lines of code, effective navigation tools are essential. Here are the tools and techniques used by kernel developers:
| Tool | Purpose | Example Usage |
|---|---|---|
cscope | Code indexing and cross-referencing | make cscope then cscope -d |
ctags/etags | Tag-based navigation for editors | make tags or make TAGS |
git grep | Fast text search in git repos | git grep -n 'schedule()' |
git log | History of changes to a file/function | git log -p mm/page_alloc.c |
git blame | Line-by-line authorship | git blame kernel/sched/core.c |
| LXR/Elixir | Web-based cross-reference | https://elixir.bootlin.com/linux/latest |
./scripts/get_maintainer.pl | Find file maintainers | ./scripts/get_maintainer.pl -f mm/oom_kill.c |
12345678910111213141516171819202122232425262728
# Building navigation databases$ make cscope # Build cscope database$ make tags # Build ctags database # Using cscope interactively$ cscope -d # Launch cscope# Press Ctrl+\ to query:# 0 = Find symbol# 1 = Find global definition# 3 = Find functions calling this function# 4 = Find text string # Git-based searching (very fast)$ git grep -n 'kmalloc' # Find all kmalloc calls$ git grep -n --function 'mutex_lock' kernel/ # Show function context$ git grep -W 'EXPORT_SYMBOL' # Show whole functions containing EXPORT_SYMBOL # Finding related changes$ git log --oneline mm/slub.c # History of slab allocator$ git log -p -S 'schedule' # Commits that add/remove 'schedule'$ git log --author='Linus' --since='2023-01-01' --oneline # Finding function definitions$ grep -rn 'asmlinkage.*sys_open' fs/$ grep -rn '^SYSCALL_DEFINE' fs/open.c # Using ripgrep (faster than grep)$ rg 'struct task_struct' include/For web-based exploration, https://elixir.bootlin.com/linux/latest/source is invaluable. It provides instant cross-referencing—click any symbol to see its definition and all references. You can explore any kernel version, compare changes across versions, and navigate without needing a local checkout.
What's Next:
With the source structure mapped, we'll explore Kernel Configuration—how to navigate the thousands of options, understand what they do, and build a kernel tailored to your needs.
You now have a mental map of the Linux kernel source tree. This organization has evolved over 30 years of collaborative development, and understanding it is your first step toward reading, debugging, or contributing to the world's most important open-source project.