Stage 3 · Build
Linux Kernel Internals
Kernel Modules & eBPF
Writing kernel modules, loading code at runtime, and the eBPF revolution in observability.
Kernel Modules
Kernel modules are pieces of code that can be loaded and unloaded into the kernel at runtime. They extend kernel functionality without rebooting — adding device drivers, filesystem support, or security hooks. The module subsystem is one of Linux's most powerful extensibility mechanisms.
# List loaded modules
lsmod | head -20
# Module Size Used by
# nf_conntrack 172032 6 nf_nat,nf_conntrack_netlink,nf_conntrack_ftp
# overlay 73728 2
# veth 20480 0
# Get detailed info about a module
modinfo e1000e | head -15
# Load a module
sudo modprobe bonding mode=4
# Remove a module
sudo modprobe -r bonding
# Find module providing a file
modprobe --show-depends ext4modprobe handles module dependencies automatically, loading prerequisite modules. insmod loads a single module without resolving dependencies.
Module Lifecycle
A module goes through several states during its lifetime. Understanding these states helps with debugging and system administration.
- Live — Module is loaded and running in kernel space
- Coming — Module is being loaded, initialization not complete
- Going — Module is being removed, cleanup in progress
- Disabled — Module exists but is temporarily disabled
# View module references (what uses this module)
lsmod | grep ext4
# ext4 921600 2
# The second number (2) shows refcount — modules cannot
# be removed while in use
# Module autoloading configuration
cat /etc/modules-load.d/*.conf
# virtio
# virtio_net
# virtio_blk
# Trigger module loading for a device
sudo modprobe ext4 # Manual autoload
udevadm trigger # Let udev auto-detect and loadThe kernel can auto-load modules when it detects hardware or filesystem types. /etc/modules-load.d/ lists modules loaded at boot.
Writing a Module
A minimal kernel module requires two functions: init (loaded) and exit (unloaded). The module must be compiled against the kernel headers and linked against the kernel symbol table.
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
static int __init hello_init(void)
{
printk(KERN_INFO "Hello, kernel!\n");
return 0;
}
static void __exit hello_exit(void)
{
printk(KERN_INFO "Goodbye, kernel!\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple kernel module");module_init and module_exit register the entry/exit functions. MODULE_LICENSE is required — without it, the module taints the kernel and may not access certain symbols.
# Create Makefile
cat > Makefile << 'EOF'
obj-m += hello.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
EOF
# Build
make
# Load and check
sudo insmod hello.ko
dmesg | tail -1
# [12345.678] Hello, kernel!
# Remove
sudo rmmod hello
dmesg | tail -1
# [12345.679] Goodbye, kernel!The Makefile tells the kernel build system to compile hello.c into a loadable module (hello.ko). The -C flag changes to the kernel source directory before building.
Module Signing
Modern kernels require modules to be signed with a valid key. This prevents loading malicious or tampered modules. Unsigned modules trigger a kernel taint and may be blocked entirely.
# Check if module signing is enforced
cat /proc/sys/kernel/modules_disabled
# 0 = not disabled, 1 = no new modules can be loaded
# Check signing requirement
zgrep MODULE_SIG /proc/config.gz
# CONFIG_MODULE_SIG=y
# CONFIG_MODULE_SIG_FORCE=y <- modules must be signed
# Sign a module with a self-signed key
scripts/sign-file sha256 certs/signing_key.pem certs/signing_key.x509 hello.ko
# Disable module signature checking (debug only!)
# Add module.sig_enforce=0 to kernel command lineIn production, always keep module signing enabled. Disabling it weakens the security of the entire system.
The eBPF Revolution
eBPF (extended Berkeley Packet Filter) is a virtual machine built into the kernel that lets you run sandboxed programs at runtime without loading kernel modules. It powers modern observability tools like bpftrace, Falco, and Cilium.
# Check eBPF support
bpftool feature probe | head -10
# bpf_probe_read: yes
# bpf_trace_printk: yes
# bpf_override_return: no
# List loaded eBPF programs
bpftool prog list | head -10
# List eBPF maps (data structures)
bpftool map list | head -5
# Quick eBPF trace with bpftrace
sudo bpftrace -e 'tracepoint:syscalls:sys_enter_openat { printf("%s %s\n", comm, str(args->filename)); }'eBPF programs are verified by the kernel before loading. The verifier ensures they always terminate, access memory safely, and do not crash the kernel.
eBPF vs Kernel Modules
| Feature | Kernel Modules | eBPF |
|---|---|---|
| Language | C | C, Rust, restricted |
| Verification | Manual | Automatic (verifier) |
| Crash risk | Can kernel panic | Cannot crash kernel |
| Unload | Can be stuck if in use | Always unloadable |
| Security | Full kernel access | Sandboxed |
| Performance | Native kernel code | Near-native (JIT compiled) |
| Persistence | Requires modprobe | Programmatic load/unload |
For observability, tracing, and networking tasks, eBPF is almost always the better choice. It provides kernel-level capabilities without the risk and complexity of writing and maintaining kernel modules.
Modules compiled for one kernel version will not load on a different version. Always recompile modules after kernel upgrades. This is a common source of boot failures on systems with custom drivers.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.