Kernel Compilation & Custom Kernel Modules
Chapter 47: Kernel Compilation & Custom Kernel Modules
Section titled “Chapter 47: Kernel Compilation & Custom Kernel Modules”Learning Objectives
Section titled “Learning Objectives”- Understand when and why to compile a custom kernel
- Configure, build, and install a custom Linux kernel
- Write, compile, and load a basic kernel module
- Debug kernel module issues safely
What is this? (Beginner On-Ramp)
Section titled “What is this? (Beginner On-Ramp)”Kernel compilation is the process of building the Linux operating system from its raw source code. While most people use pre-built kernels, compiling it yourself allows you to strip out unnecessary features, making the system faster, smaller, and more secure.
47.1 Why Compile a Custom Kernel?
Section titled “47.1 Why Compile a Custom Kernel?” When to Compile a Custom Kernel ─────────────────────────────────
GOOD reasons: ✓ Enable a feature not in distro kernel (e.g., new eBPF feature) ✓ Disable features for performance/security (reduce attack surface) ✓ Backport a specific patch from newer kernel ✓ Kernel development / testing ✓ Real-time kernel (PREEMPT_RT) for low-latency systems ✓ Embedded systems (minimal kernel for specific hardware) ✓ Custom drivers for unsupported hardware
BAD reasons (avoid): ✗ "Optimize for my hardware" (distro kernels already do this) ✗ Marginal performance gains (not worth maintenance burden) ✗ Just to say you did it (in production, stability > custom)
Reality: 99% of production systems use distro kernels. Custom kernels = complex, lose vendor support, manual patching. Use ONLY when there's a specific technical need.47.2 Kernel Configuration
Section titled “47.2 Kernel Configuration”# ── Get Kernel Source ─────────────────────────────────────# From kernel.orgwget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.6.tar.xztar -xf linux-6.6.tar.xzcd linux-6.6
# Install build dependenciesapt install build-essential libncurses-dev libssl-dev \ libelf-dev bison flex dwarves bc
# ── Configuration Approaches ──────────────────────────────
# 1. Start from distro config (safest)cp /boot/config-$(uname -r) .configmake olddefconfig # Accept defaults for new options
# 2. Interactive menuconfigmake menuconfig# Navigate with arrows, Y=built-in, M=module, N=disabled# Key sections:# General Setup → kernel features# Processor type → architecture optimization# Networking → protocols, filtering# Security → LSM, seccomp, capabilities# File systems → which FS to support
# 3. Minimal config (good for understanding, not production)make allnoconfig # All options disabledmake tinyconfig # Minimal bootable config
# ── Important Config Options ──────────────────────────────# Security hardening:CONFIG_SECURITY_DMESG_RESTRICT=y # Restrict dmesg to rootCONFIG_STRICT_KERNEL_RWX=y # W^X enforcementCONFIG_RANDOMIZE_BASE=y # KASLRCONFIG_STACKPROTECTOR_STRONG=y # Stack canaries
# Performance options:CONFIG_HZ=1000 # 1000 Hz timer (lower latency)CONFIG_NO_HZ_FULL=y # Tickless for isolated CPUs
# Debug options (never in production!):# CONFIG_KASAN=y # Memory sanitizer (huge overhead)# CONFIG_LOCKDEP=y # Lock dependency checker47.3 Building and Installing
Section titled “47.3 Building and Installing”# ── Build the Kernel ──────────────────────────────────────# Get CPU countNCPUS=$(nproc)
# Build (takes 20-60 minutes depending on hardware)make -j$NCPUS 2>&1 | tee build.log
# Build modules onlymake -j$NCPUS modules
# ── Install ───────────────────────────────────────────────# Install modulessudo make modules_install# Installs to: /lib/modules/6.6.0/
# Install kernelsudo make install# Installs: /boot/vmlinuz-6.6.0# /boot/System.map-6.6.0# /boot/config-6.6.0
# Generate initramfssudo update-initramfs -c -k 6.6.0 # Debian/Ubuntusudo dracut --force "" 6.6.0 # RHEL/CentOS
# Update bootloadersudo update-grub # Debian/Ubuntusudo grub2-mkconfig -o /boot/grub2/grub.cfg # RHEL
# Reboot into new kernelsudo reboot
# Verifyuname -r# 6.6.0
# ── Build a .deb Package (Debian/Ubuntu) ──────────────────make -j$NCPUS bindeb-pkg LOCALVERSION=-custom# Creates: linux-image-6.6.0-custom_*.deb# Install on any Debian system: dpkg -i linux-image-*.deb47.4 Writing a Kernel Module
Section titled “47.4 Writing a Kernel Module”// hello_module.c — Minimal "Hello World" kernel module
#include <linux/init.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/proc_fs.h>#include <linux/uaccess.h>
MODULE_LICENSE("GPL");MODULE_AUTHOR("Your Name");MODULE_DESCRIPTION("A minimal hello world kernel module");MODULE_VERSION("1.0");
// ── Module Load Function ──────────────────────────────────static int __init hello_init(void){ // pr_info: writes to kernel ring buffer (dmesg) pr_info("hello_module: loaded! PID of loader: %d\n", current->pid); pr_info("hello_module: running on CPU %d\n", smp_processor_id()); return 0; // 0 = success, negative = error}
// ── Module Unload Function ────────────────────────────────static void __exit hello_exit(void){ pr_info("hello_module: unloaded\n");}
// Register init and exit functionsmodule_init(hello_init);module_exit(hello_exit);# Makefile for kernel moduleobj-m += hello_module.o
# Kernel build directoryKDIR := /lib/modules/$(shell uname -r)/build
all: $(MAKE) -C $(KDIR) M=$(PWD) modules
clean: $(MAKE) -C $(KDIR) M=$(PWD) clean# ── Build and Load ────────────────────────────────────────make# Creates: hello_module.ko
# Inspect the modulemodinfo hello_module.ko# filename: hello_module.ko# license: GPL# description: A minimal hello world kernel module# author: Your Name# depends: (none)
# Load the modulesudo insmod hello_module.ko# OR: sudo modprobe hello_module (searches /lib/modules/)
# Verify it's loadedlsmod | grep hello# hello_module 16384 0
# Check kernel log outputdmesg | tail -3# [12345.678] hello_module: loaded! PID of loader: 1234# [12345.679] hello_module: running on CPU 2
# Unloadsudo rmmod hello_moduledmesg | tail -1# [12360.123] hello_module: unloaded
# Make persistent (load on boot)sudo cp hello_module.ko /lib/modules/$(uname -r)/kernel/drivers/misc/sudo depmod -aecho "hello_module" | sudo tee /etc/modules-load.d/hello.conf47.5 A Practical Module: /proc Interface
Section titled “47.5 A Practical Module: /proc Interface”// proc_module.c — Kernel module exposing data via /proc
#include <linux/init.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/proc_fs.h>#include <linux/seq_file.h>
MODULE_LICENSE("GPL");
static struct proc_dir_entry *proc_entry;
// Called when user reads /proc/mymodulestatic int mymodule_show(struct seq_file *m, void *v){ seq_printf(m, "Hello from kernel module!\n"); seq_printf(m, "Kernel version: %s\n", UTS_RELEASE); seq_printf(m, "Uptime (jiffies): %lu\n", jiffies); seq_printf(m, "Current PID: %d\n", current->pid); return 0;}
static int mymodule_open(struct inode *inode, struct file *file){ return single_open(file, mymodule_show, NULL);}
static const struct proc_ops mymodule_fops = { .proc_open = mymodule_open, .proc_read = seq_read, .proc_lseek = seq_lseek, .proc_release = single_release,};
static int __init mymodule_init(void){ proc_entry = proc_create("mymodule", 0444, NULL, &mymodule_fops); if (!proc_entry) { pr_err("mymodule: failed to create /proc/mymodule\n"); return -ENOMEM; } pr_info("mymodule: /proc/mymodule created\n"); return 0;}
static void __exit mymodule_exit(void){ proc_remove(proc_entry); pr_info("mymodule: /proc/mymodule removed\n");}
module_init(mymodule_init);module_exit(mymodule_exit);# After loading:cat /proc/mymodule# Hello from kernel module!# Kernel version: 6.6.0# Uptime (jiffies): 12345678# Current PID: 543247.6 Interview Questions
Section titled “47.6 Interview Questions”Q1: What is the difference between a built-in kernel feature and a kernel module?
Answer: A built-in feature (compiled with
Y) is permanently linked into the kernel image (vmlinux/bzImage). It’s always available, loaded at boot, and cannot be unloaded. Use for core functionality needed at boot time (root filesystem driver, basic networking). A kernel module (compiled withM) is a separate.kofile loaded at runtime withinsmod/modprobeand can be unloaded withrmmod. Modules extend the kernel without requiring reboot — device drivers, filesystem support, networking filters. Trade-offs: built-ins slightly faster (no module loading overhead), modules give flexibility and smaller kernel size. Security consideration: modules can be loaded by root —CONFIG_MODULE_SIG_FORCErequires module signatures to prevent loading unsigned code.
47.7 Summary
Section titled “47.7 Summary”| Task | Command |
|---|---|
| Configure kernel | make menuconfig |
| Build kernel | make -j$(nproc) |
| Install | make install && update-initramfs |
| Build module | make in module directory |
| Load module | insmod module.ko or modprobe module |
| Unload module | rmmod module |
| List modules | lsmod |
| Module info | modinfo module.ko |
Next Chapter: Chapter 48: Advanced eBPF: Writing Programs & Tracing
Section titled “Next Chapter: Chapter 48: Advanced eBPF: Writing Programs & Tracing”Prerequisites
Section titled “Prerequisites”Chapter 1 (Architecture), strong C programming basics.
Advantages & Disadvantages
Section titled “Advantages & Disadvantages”Advantages: Strips out unused bloat, optimizes for specific hardware architectures. Disadvantages: Takes hours to compile, manual maintenance of patches is a nightmare.
Common Mistakes
Section titled “Common Mistakes”- Compiling with all features as built-in (
=y) instead of modules (=m), resulting in a bloated, slow kernel. - Forgetting to sign the kernel or modules when Secure Boot is enabled.
- Not saving the
.configfile, losing the custom configuration for future rebuilds.
Troubleshooting
Section titled “Troubleshooting”| Symptom | Cause | Diagnosis | Fix |
|---|---|---|---|
| Kernel panic on boot (VFS unable to mount root) | Filesystem driver built as module but missing from initramfs | Boot previous kernel, check .config | Ensure root FS driver (e.g., ext4) is built-in (=y) or properly added to initramfs |
| Make fails with missing headers/libraries | Build dependencies not installed | Read the make error output | Install libelf-dev, build-essential, flex, bison, libssl-dev |
Hands-on Lab
Section titled “Hands-on Lab”Objective: Configure a kernel build.
# 1. Download kernel source# wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.x.x.tar.xz
# 2. Copy current system config as a base# cp /boot/config-$(uname -r) .config
# 3. Open the menu-based configuration tool# make menuconfigExercises
Section titled “Exercises”- Compile a custom kernel from source. Boot into it and verify with
uname -r. - Write a simple ‘Hello World’ out-of-tree kernel module, compile it using a Makefile, and load it with
insmod.
Revision Notes
Section titled “Revision Notes”make menuconfigis the standard tool for configuring kernel options.=ycompiles into the main vmlinuz image;=mcompiles as a separate.komodule.- DKMS (Dynamic Kernel Module Support) automatically recompiles out-of-tree modules when the kernel is upgraded.
- Custom kernels are often needed in HFT (High-Frequency Trading) or embedded systems to strip out unnecessary bloat.
Further Reading
Section titled “Further Reading”- Linux Kernel in a Nutshell by Greg Kroah-Hartman
- Kernel.org documentation
Related Chapters
Section titled “Related Chapters”- Chapter 9 — Device Drivers
Last Updated: July 2026