Skip to content

Kernel Compilation & Custom Kernel Modules

Chapter 47: Kernel Compilation & Custom Kernel Modules

Section titled “Chapter 47: Kernel Compilation & Custom Kernel Modules”
  • 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

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.

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.

Terminal window
# ── Get Kernel Source ─────────────────────────────────────
# From kernel.org
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.6.tar.xz
tar -xf linux-6.6.tar.xz
cd linux-6.6
# Install build dependencies
apt 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) .config
make olddefconfig # Accept defaults for new options
# 2. Interactive menuconfig
make 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 disabled
make tinyconfig # Minimal bootable config
# ── Important Config Options ──────────────────────────────
# Security hardening:
CONFIG_SECURITY_DMESG_RESTRICT=y # Restrict dmesg to root
CONFIG_STRICT_KERNEL_RWX=y # W^X enforcement
CONFIG_RANDOMIZE_BASE=y # KASLR
CONFIG_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 checker

Terminal window
# ── Build the Kernel ──────────────────────────────────────
# Get CPU count
NCPUS=$(nproc)
# Build (takes 20-60 minutes depending on hardware)
make -j$NCPUS 2>&1 | tee build.log
# Build modules only
make -j$NCPUS modules
# ── Install ───────────────────────────────────────────────
# Install modules
sudo make modules_install
# Installs to: /lib/modules/6.6.0/
# Install kernel
sudo make install
# Installs: /boot/vmlinuz-6.6.0
# /boot/System.map-6.6.0
# /boot/config-6.6.0
# Generate initramfs
sudo update-initramfs -c -k 6.6.0 # Debian/Ubuntu
sudo dracut --force "" 6.6.0 # RHEL/CentOS
# Update bootloader
sudo update-grub # Debian/Ubuntu
sudo grub2-mkconfig -o /boot/grub2/grub.cfg # RHEL
# Reboot into new kernel
sudo reboot
# Verify
uname -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-*.deb

// 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 functions
module_init(hello_init);
module_exit(hello_exit);
# Makefile for kernel module
obj-m += hello_module.o
# Kernel build directory
KDIR := /lib/modules/$(shell uname -r)/build
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean
Terminal window
# ── Build and Load ────────────────────────────────────────
make
# Creates: hello_module.ko
# Inspect the module
modinfo hello_module.ko
# filename: hello_module.ko
# license: GPL
# description: A minimal hello world kernel module
# author: Your Name
# depends: (none)
# Load the module
sudo insmod hello_module.ko
# OR: sudo modprobe hello_module (searches /lib/modules/)
# Verify it's loaded
lsmod | grep hello
# hello_module 16384 0
# Check kernel log output
dmesg | tail -3
# [12345.678] hello_module: loaded! PID of loader: 1234
# [12345.679] hello_module: running on CPU 2
# Unload
sudo rmmod hello_module
dmesg | 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 -a
echo "hello_module" | sudo tee /etc/modules-load.d/hello.conf

// 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/mymodule
static 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);
Terminal window
# After loading:
cat /proc/mymodule
# Hello from kernel module!
# Kernel version: 6.6.0
# Uptime (jiffies): 12345678
# Current PID: 5432

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 with M) is a separate .ko file loaded at runtime with insmod/modprobe and can be unloaded with rmmod. 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_FORCE requires module signatures to prevent loading unsigned code.


TaskCommand
Configure kernelmake menuconfig
Build kernelmake -j$(nproc)
Installmake install && update-initramfs
Build modulemake in module directory
Load moduleinsmod module.ko or modprobe module
Unload modulermmod module
List moduleslsmod
Module infomodinfo module.ko

Chapter 1 (Architecture), strong C programming basics.


Advantages: Strips out unused bloat, optimizes for specific hardware architectures. Disadvantages: Takes hours to compile, manual maintenance of patches is a nightmare.


  • 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 .config file, losing the custom configuration for future rebuilds.

SymptomCauseDiagnosisFix
Kernel panic on boot (VFS unable to mount root)Filesystem driver built as module but missing from initramfsBoot previous kernel, check .configEnsure root FS driver (e.g., ext4) is built-in (=y) or properly added to initramfs
Make fails with missing headers/librariesBuild dependencies not installedRead the make error outputInstall libelf-dev, build-essential, flex, bison, libssl-dev

Objective: Configure a kernel build.

Terminal window
# 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 menuconfig

  1. Compile a custom kernel from source. Boot into it and verify with uname -r.
  2. Write a simple ‘Hello World’ out-of-tree kernel module, compile it using a Makefile, and load it with insmod.

  • make menuconfig is the standard tool for configuring kernel options.
  • =y compiles into the main vmlinuz image; =m compiles as a separate .ko module.
  • 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.

  • Linux Kernel in a Nutshell by Greg Kroah-Hartman
  • Kernel.org documentation


Last Updated: July 2026