Linux Kernel Modules (LKM): Architecture, Concepts & Framework

Linux Kernel Modules (LKM) – Free Linux Kernel Programming Course | EmbeddedPathashala

Linux Kernel Modules (LKM)
Part 1 — Architecture, Concepts & the LKM Framework | Updated for Linux 6.x
🆓 100% Free
🐧 Linux 6.x
🔰 Beginner Friendly
⏱ ~20 min read
🔑 Key Topics in This Part
Loadable Kernel Module Kernel Space vs User Space LKM Framework Static vs Dynamic Modules Monolithic Kernel Kernel Architecture Device Drivers Linux 6.x

Welcome to the free Linux Kernel Programming course on EmbeddedPathashala. This series takes you from zero to writing your own kernel modules and device drivers on Linux 6.x. No prior kernel experience needed — just solid C knowledge and curiosity.

In this first part, you will understand why kernel modules exist, how the Linux kernel is structured, and what happens when a module gets loaded into a running system.

1. What Is the Linux Kernel?

The Linux kernel is the core piece of software that sits between your hardware and your applications. Every time your program reads a file, opens a network socket, or allocates memory, it talks to the kernel through a layer called system calls. The kernel then carries out the actual work — talking to the disk controller, the network interface, or the memory management unit.

The kernel runs in a privileged CPU mode called kernel space. Your programs run in user space, which has restricted access to hardware. This separation is a fundamental security boundary.

Linux System Layers — Kernel Space vs User Space
USER SPACE
Applications  |  Shell  |  Libraries (glibc)
⬆ System Call Interface (read, write, open, ioctl …) ⬇
KERNEL SPACE
Virtual File System (VFS) Network Stack Memory Manager
🔌 Loadable Kernel Modules (Device Drivers, Filesystems, Network Protocols)
⚙ Hardware — CPU  |  RAM  |  Storage  |  Network Interface  |  Peripherals
Note: Code running in kernel space has full access to all hardware and memory. A bug in kernel code can crash the entire system — unlike a bug in user-space code that only kills that one process. This is why kernel programming requires extra care.

2. Static vs Dynamic Kernel Modules

When you build the Linux kernel, every feature you enable gets compiled in one of two ways:

Built-in (Static) — compiled directly into the kernel image

These features are always present. They get loaded at boot time as part of the main kernel image (vmlinuz). You cannot remove them at runtime. If a feature is built-in, it consumes memory even when not in use.

In a .config file, this looks like:

CONFIG_EXT4_FS=y   # y means built-in
Loadable Module (Dynamic) — compiled as a separate .ko file

These are separate binary files that live on disk and can be inserted into or removed from a running kernel at any time. The kernel does not need to be rebooted. When loaded, a module becomes a full part of kernel space.

In a .config file, this looks like:

CONFIG_EXT4_FS=m   # m means loadable module
Static Module vs Loadable Kernel Module — Build Output
Property Built-in (=y) Loadable (=m)
Output file Inside vmlinuz mydriver.ko
Load time Boot only Any time (runtime)
Can unload? No Yes (if use count = 0)
Reboot needed to update? Yes No
Memory usage Always in RAM Only when loaded

3. What Can a Kernel Module Do?

A kernel module can add almost any feature to the kernel at runtime. The most common uses are:

🔌 Device Drivers

Tell the kernel how to talk to a specific piece of hardware — a USB device, a network card, a sensor on I2C, or a custom FPGA peripheral. Almost every hardware driver you will ever write is a kernel module.

📁 Filesystem Drivers

Teach the kernel how to read and write a specific filesystem format. ext4, FAT32, NTFS support on Linux — each is a kernel module that plugs into the Virtual File System (VFS) layer.

🌐 Network Protocol Modules

Add support for network protocols or packet filtering rules. Netfilter (the engine behind iptables and nftables) is loaded as kernel modules.

⚙ Kernel Extensions / Utilities

Anything that needs to run in kernel space: crypto algorithms, compression engines, scheduler plugins, or even custom system call extensions for research purposes.

4. The LKM Framework — How It Works

The Linux kernel provides a framework for managing loadable modules. When you compile a module, it becomes a .ko file (Kernel Object). This file is a specially formatted ELF binary that contains not just compiled code but also metadata — the kernel version it was built against, its license, its author, and its dependencies.

Lifecycle of a Kernel Module — From Source to Running
📄 mydriver.c
Source code written by you
⚙ make
Kernel build system (kbuild)
📦 mydriver.ko
Kernel Object binary
sudo insmod mydriver.ko
OR: modprobe mydriver
🚀 Module runs in kernel space
init function called, hardware initialized
sudo rmmod mydriver
exit function called, resources freed

When you run insmod or modprobe, the kernel’s module loader takes the .ko file, verifies its kernel version matches the running kernel, resolves any symbol dependencies, copies the code into kernel memory, and then calls the module’s init function. From that point, the module is part of the kernel.

insmod vs modprobe — Key Difference:
insmod loads one specific .ko file by path. It does not automatically resolve dependencies — if your module needs another module to be loaded first, you must do that yourself.

modprobe is smarter. It looks up the module by name in /lib/modules/$(uname -r)/, automatically loads all dependencies in the correct order, and uses /etc/modprobe.d/ configuration if present. In production, always prefer modprobe.

5. Kernel Version Matching — Why It Matters

Every .ko file contains a vermagic string — a fingerprint that records exactly which kernel version and configuration it was built for. When you try to load a module, the kernel checks this string against itself. If they don’t match, the load fails with a “version magic mismatch” error.

# Check the vermagic of a .ko file
modinfo mydriver.ko

# Sample output:
filename:       mydriver.ko
license:        GPL
description:    My first kernel module
author:         Ravi Kumar
vermagic:       6.8.0-45-generic SMP preempt mod_unload
depends:

This is a safety mechanism. Kernel internals (data structures, function signatures, exported symbols) change between versions. A module compiled for kernel 5.15 might use a struct field that no longer exists in kernel 6.8 — loading it would corrupt memory. The vermagic check prevents this.

6. Where Modules Live on Disk

Installed kernel modules live under /lib/modules/, organized by kernel version:

/lib/modules/
└── 6.8.0-45-generic/          ← one folder per installed kernel version
    ├── kernel/                 ← modules shipped with the kernel
    │   ├── drivers/
    │   │   ├── net/
    │   │   ├── usb/
    │   │   └── char/
    │   └── fs/
    ├── modules.dep             ← dependency map (generated by depmod)
    ├── modules.order           ← build order of modules
    └── modules.builtin         ← modules built statically (=y)

The modules.dep file is what allows modprobe to resolve dependencies. It is generated (or regenerated) by running depmod -a after you install a new module.

Quick Tip: To see all currently loaded modules, run lsmod. The output shows module name, size in memory, use count, and which other modules depend on it. A module can only be removed when its use count is zero.

7. What You Need to Start Writing Kernel Modules

Before writing a single line of kernel code, your development machine needs the following:

Linux Kernel Headers

The .h header files that define all kernel data structures, macros, and function prototypes. You do not need the full kernel source tree — just the headers matching your running kernel.

# Ubuntu / Debian
sudo apt install linux-headers-$(uname -r)

# Fedora / RHEL
sudo dnf install kernel-devel-$(uname -r)
GCC and Build Tools
# Ubuntu / Debian
sudo apt install build-essential

# Fedora
sudo dnf groupinstall "Development Tools"
Verify Your Setup
# Check kernel version
uname -r

# Confirm headers are present
ls /lib/modules/$(uname -r)/build

# Check gcc
gcc --version
⚠ Root Required: Loading and unloading kernel modules requires root privileges. Always use sudo with insmod, rmmod, and modprobe. A buggy module can crash the system, so test on a virtual machine (QEMU, VirtualBox, VMware) before running on real hardware.

8. Essential Module Management Commands

Quick Reference — Module Commands
Command What It Does
lsmod List all currently loaded modules
sudo insmod mymod.ko Load a module by file path (no dependency resolution)
sudo modprobe mymod Load by name, auto-resolves dependencies
sudo rmmod mymod Unload a module (fails if use count > 0)
sudo modprobe -r mymod Unload + automatically remove unused dependencies
modinfo mymod.ko Show module metadata (vermagic, license, parameters)
dmesg | tail -20 View recent kernel log messages (from printk)
cat /proc/modules Raw list of loaded modules with addresses

🎯 Interview Questions — LKM Fundamentals

Q1. What is a Loadable Kernel Module and how does it differ from a built-in kernel feature?
An LKM is a compiled binary (.ko file) that can be inserted into or removed from a running Linux kernel without rebooting. A built-in feature is permanently compiled into the kernel image (vmlinuz) and always present from boot. LKMs save memory and allow updates without reboots.
Q2. What is vermagic and why does the kernel check it?
vermagic is a string embedded in every .ko file that records the kernel version, SMP setting, and other build flags used when the module was compiled. The kernel checks it at load time to reject modules built for a different kernel version, preventing memory corruption from ABI mismatches.
Q3. What is the difference between insmod and modprobe?
insmod loads a single .ko file by its file path and does not resolve dependencies. modprobe loads a module by name, looks it up in /lib/modules/, automatically loads all required dependency modules in correct order, and respects /etc/modprobe.d/ configuration. Always prefer modprobe in production.
Q4. Why can’t you unload a module when its use count is greater than zero?
The use count tracks how many other modules or open file descriptors depend on this module being present. Removing it while something depends on it would leave dangling pointers in kernel memory, leading to a kernel panic. The kernel enforces this to maintain memory safety.
Q5. What happens internally when you run sudo insmod mydriver.ko?
The kernel reads the .ko ELF file, verifies the vermagic string, resolves all undefined symbols against exported kernel symbols, copies the module code and data into kernel memory, performs relocation, and then calls the module’s init function (registered via module_init()). From this point, the module is a live part of the kernel.
Q6. What is the role of modules.dep?
modules.dep is a dependency map generated by depmod. It lists each module and the other modules it needs. modprobe reads this file to determine which modules to load first before loading the requested module. It must be regenerated (via depmod -a) whenever new modules are installed.

✅ Part 1 Summary

In this part you learned:

  • The Linux kernel runs in privileged kernel space; user programs run in user space
  • Kernel features can be built-in (=y) or compiled as loadable modules (=m)
  • A .ko file is the compiled output of a kernel module — a special ELF binary
  • insmod loads by file path; modprobe loads by name with dependency resolution
  • The vermagic string ensures modules are only loaded into the exact kernel they were built for
  • Modules live in /lib/modules/$(uname -r)/ and use count must be zero to unload
Ready to write your first kernel module?

In Part 2, you will write a complete kernel module from scratch, understand the init/exit lifecycle, and load it into a running Linux 6.x system.

→ Continue to Part 2: Your First Kernel Module

EmbeddedPathashala — Free Linux Kernel Programming Course | Updated for Linux 6.x

Leave a Reply

Your email address will not be published. Required fields are marked *