What are Linux Kernel Modules (LKM)

Prev_Lec

Next_Lec
What are Linux Kernel Modules (LKM) – Kernel Modules Introduction

What are Linux Kernel Modules (LKM)
What are Linux Kernel Modules (LKM) – What They Are, Where They Live, and How to Inspect Them
⏱ 20 min read
🐧 Kernel 6.x
🆓 100% Free
🎯 Beginner Friendly

Keywords:

loadable kernel module
LKM
kernel module
lsmod
modinfo
insmod
rmmod
modprobe
.ko file
linux device driver
kernel space
vermagic

What Exactly is a Kernel Module?

Think about how a smartphone works. You can install and uninstall apps without rebooting the phone. The Linux kernel has a very similar concept, but at the operating system level — it is called a Loadable Kernel Module, or LKM for short.

A kernel module is a piece of C code that you can plug into the running Linux kernel and unplug from it, all without ever rebooting the machine. The moment it is loaded, it becomes part of the kernel and runs with the same privileges as the kernel itself — that means full, unrestricted access to hardware, memory, and every kernel data structure.

Key insight: Once an LKM is loaded, it is NOT separate from the kernel. It runs inside kernel space. If your module has a bug that causes a NULL pointer dereference, it can crash the entire system — just like a bug inside the kernel itself.

Kernel Space vs User Space – Where LKMs Live
🔵 KERNEL SPACE (Ring 0)
Core Kernel
(vmlinuz)
⚡ LKM – hello.ko
(loaded at runtime)
Device Drivers
(built-in)
VFS / FS
Subsystem
Full hardware access · No memory protection between kernel components
─── SYSTEM CALL BOUNDARY ───
🟢 USER SPACE (Ring 3)
bash / shell
insmod / rmmod
your apps
Protected · Cannot directly touch hardware

The diagram above shows the big picture. User-space programs like insmod and rmmod are the tools you use from the terminal. But the actual kernel module code — your .ko file — runs inside kernel space once loaded. This is very different from writing a normal C program.

Why Not Just Compile Everything Into the Kernel?

Early Linux kernels were what we call monolithic — all drivers and subsystems were baked permanently into the kernel binary. This caused two problems. First, the kernel image became huge, wasting RAM with code for hardware you don’t even have. Second, adding support for new hardware required recompiling and rebooting the kernel — impractical on production servers or embedded devices.

LKMs solve both problems elegantly. Only the modules you actually need get loaded. A web server doesn’t need a GPU driver loaded in memory. A development machine can load and unload an experimental driver fifty times in a row without a single reboot.

Where Do Kernel Modules Live on Disk?

Every kernel module is stored as a .ko file (kernel object). On a modern Linux system, all installed modules for the currently running kernel live under a single directory tree:

/lib/modules/$(uname -r)/kernel/

The $(uname -r) part gets replaced with your actual kernel version string. This is how the system keeps modules for multiple kernel versions separate and avoids conflicts.

Try it yourself: Open a terminal and run ls /lib/modules/$(uname -r)/kernel/ to see the top-level subdirectories of your kernel’s modules. On a modern Ubuntu 24.04 or Debian 12 system with kernel 6.x, you will see directories like arch/, crypto/, drivers/, fs/, net/, and sound/.

Module Directory Tree on a Kernel 6.x System
/lib/modules/6.8.0-xx-generic/kernel/
├── arch/ # architecture-specific modules (x86, arm, etc.)
├── crypto/ # cryptographic algorithm modules
├── drivers/ # the biggest one – all hardware drivers
│ ├── net/
│ │ └── ethernet/
│ │ ├── intel/ # e1000e.ko, igb.ko, etc.
│ │ ├── realtek/ # r8169.ko
│ │ └── broadcom/ # tg3.ko
│ ├── usb/
│ ├── gpu/
│ └── block/
├── fs/ # filesystem modules (ext4, btrfs, nfs…)
├── net/ # networking protocol modules
└── sound/ # audio drivers (ALSA)

This structure is consistent across all major Linux distributions. Notice that the drivers/ directory is by far the largest — this reflects the fact that device drivers are the most common use case for kernel modules.

Kernel 6.x vs older kernels: In kernel 6.x, module files are now compressed with .ko.zst (Zstandard) or .ko.xz format by default on many distributions, instead of plain .ko. The kernel and tools like modprobe handle the decompression transparently — you don’t have to worry about it. You may notice e1000e.ko.zst instead of e1000e.ko on modern systems.

The LKM Lifecycle – Load, Run, Unload

A kernel module goes through a very predictable lifecycle. Understanding this lifecycle is crucial before you write your first module.

LKM Lifecycle – From .ko File to Kernel and Back
📄
my_module.ko
on disk
insmod / modprobe
module_init() called
runs setup code
active in kernel
Running
handles events / IRQs
rmmod / modprobe -r
🔴
module_exit() called
cleans up resources

The lifecycle is straightforward:

Loading: You run sudo insmod my_module.ko or sudo modprobe my_module. The kernel verifies the module is compatible (through a mechanism called vermagic — more on that shortly), then copies the module code into kernel memory. The kernel then calls your module_init() function.

Running: The module is active. It might register interrupt handlers, create character devices, hook into the network stack, or simply wait to be called. Whatever it does, it does it inside kernel space.

Unloading: You run sudo rmmod my_module. The kernel calls your module_exit() function where you must clean up everything the init function set up — free memory, deregister devices, release locks. If you don’t, you get resource leaks inside the kernel.

Important: If a module is currently in use (for example, a filesystem module with a mounted volume, or a driver with an open device file), the kernel will refuse to unload it. The module’s internal use count prevents accidental removal.

Inspecting Modules – lsmod and modinfo

Linux provides a set of user-space tools specifically designed to interact with the kernel’s module system. The two most important ones for beginners are lsmod and modinfo.

lsmod – List Currently Loaded Modules

The lsmod command reads the file /proc/modules and displays a nicely formatted list of all kernel modules currently loaded in memory. Run it without any arguments:

$ lsmod
Module                  Size  Used by
snd_hda_intel         118784  2
snd_hda_codec         212992  3 snd_hda_intel
e1000e                327680  0
bluetooth             802816  10 btrtl,btintel
usbcore               327680  5 btusb,xhci_hcd
...

The output has three columns. The first column is the module name. The second is how much kernel memory it occupies (in bytes). The third shows how many other modules or processes are currently using this module — this is the reference count. A module with a non-zero reference count cannot be unloaded.

Note on kernel 6.x: The lsmod output format remains the same in kernel 6.x. The data still comes from /proc/modules. However you will notice many more modules on modern distributions because of expanded hardware support and security modules (like lockdown, apparmor).

modinfo – Deep Dive Into a Module’s Metadata

Every .ko file carries metadata embedded inside it — the author, license, description, which kernel version it was compiled for, and what parameters it accepts. The modinfo command lets you read all of this without even loading the module.

$ modinfo e1000e
filename:       /lib/modules/6.8.0-xx-generic/kernel/drivers/net/ethernet/intel/e1000e/e1000e.ko.zst
version:        3.2.6-k
license:        GPL v2
description:    Intel(R) PRO/1000 Network Driver
author:         Intel Corporation, linux.nics@intel.com
srcversion:     ...
alias:          pci:v00008086d0000...
vermagic:       6.8.0-xx-generic SMP preempt mod_unload
name:           e1000e
parm:           copybreak:Maximum size of packet copied to new buffer on rx (uint)
parm:           debug:Debug level (0=off, max is 16) (int)

Understanding modinfo Output – Field by Field
Field What It Means Why It Matters
filename Full path to the .ko file on disk Tells you exactly where the module binary lives
license GPL, MIT, Proprietary, etc. Non-GPL modules “taint” the kernel – kernel devs won’t debug tainted kernel crashes
vermagic Kernel version + config flags used at compile time Must match the running kernel exactly, otherwise loading is refused
alias Hardware IDs this module can drive (PCI, USB, etc.) udev uses this to auto-load the correct driver when you plug in hardware
parm Parameters the module accepts at load time You can pass these with insmod or in /etc/modprobe.d/ config

insmod vs modprobe – What’s the Difference?

You will hear both insmod and modprobe when working with kernel modules. They both load modules, but they work differently.

insmod vs modprobe – Side by Side
⚡ insmod
  • Low-level, simple tool
  • Needs the full path to .ko file
  • Does NOT resolve dependencies
  • If module A needs module B, you must load B first manually
  • Good for quick testing during development
sudo insmod ./my_module.ko
🔧 modprobe
  • High-level, smart tool
  • Just use the module name
  • Automatically resolves dependencies
  • Reads /etc/modprobe.d/ config files
  • Preferred for production use
sudo modprobe my_module

What Are Kernel Modules Used For?

Device drivers are the most famous use case, but kernel modules can be used for a surprisingly wide range of things. Here is an overview:

Common Use Cases for Linux Kernel Modules
🖥️
Device Drivers
NIC, GPU, USB, SSD, UART, SPI, I2C controllers
💾
Filesystems
ext4, btrfs, nfs, fat32 all loaded as modules
🔥
Network Filters
iptables / nftables rules, packet sniffers, VPN tunnels
🔐
Security
AppArmor, SELinux, kernel lockdown modules
🔭
Observability
eBPF helpers, perf counters, tracepoints
⚙️
Custom Kernel Code
Your own logic injected into the running kernel

This range of use cases is exactly why understanding LKMs is the foundation for all Linux kernel development. Whether you want to write device drivers, build a custom firewall, or develop a new filesystem — you start by understanding how kernel modules work.

Security note: Because modules run in kernel space with full privileges, they are also a vector for rootkits. Modern kernels (6.x) have Secure Boot integration and module signing. A signed module has a cryptographic signature that the kernel verifies before loading, preventing unsigned or tampered modules from entering the kernel. You can check your kernel’s signing policy with cat /proc/sys/kernel/modules_disabled.

🎯 Interview Questions – Kernel Modules

These are commonly asked in Linux kernel and embedded Linux interviews. Make sure you can answer them confidently.

Q1. What is a Loadable Kernel Module (LKM)? How is it different from code built into the kernel?
An LKM is a piece of C code packaged as a .ko (kernel object) file that can be inserted into the running Linux kernel without rebooting. Code built directly into the kernel (compiled with obj-y in Kconfig) is always present in memory. An LKM compiled with obj-m only consumes memory when loaded. Both run in kernel space with equal privilege once the module is loaded.
Q2. What is “vermagic” in a kernel module? Why does it matter?
Vermagic is a string embedded inside every .ko file that records the exact kernel version, CPU architecture, and key build flags (SMP, preempt, modversions) at compile time. The kernel checks this string on every insmod or modprobe. If it doesn’t match the running kernel, the load is rejected with “disagrees about version of symbol” or similar error. This prevents loading a module compiled for kernel 5.x into a kernel 6.x system.
Q3. What is the difference between insmod and modprobe?
insmod is a simple, low-level tool that loads a single .ko file from a given path. It does not handle dependencies. modprobe is a higher-level tool that accepts a module name, automatically resolves dependencies using modules.dep, reads configuration from /etc/modprobe.d/, and can also handle module aliases. For production use and scripts, always prefer modprobe.
Q4. What happens if a module’s cleanup function (module_exit) is not implemented properly?
If module_exit() doesn’t properly undo what module_init() set up, you get resource leaks inside the kernel — un-freed memory, registered but inaccessible devices, stuck interrupt handlers. Since the kernel has no garbage collection, these leaks persist until reboot. Worst case, attempting to unload a partially-cleaned module can cause a kernel panic.
Q5. What does it mean when the kernel is “tainted”?
When a non-GPL or proprietary module is loaded, the kernel sets a “taint” flag. You can see the taint state with cat /proc/sys/kernel/tainted. A non-zero value means the kernel is tainted. Kernel maintainers typically won’t investigate crash reports from tainted kernels because the proprietary module may have corrupted kernel data structures in ways that cannot be audited.
Q6. Where does lsmod get its information from?
lsmod reads the virtual file /proc/modules, which is maintained by the kernel and updated in real time as modules are loaded and unloaded. Each line represents one loaded module with its size and reference count.
Q7. Can a kernel module be loaded if the kernel is running with Secure Boot enabled?
On a system with Secure Boot and kernel module signing enforced (CONFIG_MODULE_SIG_FORCE=y), only modules with a valid cryptographic signature matching the kernel’s trusted key can be loaded. An unsigned or self-signed module will be rejected. On kernel 6.x with lockdown mode active, even root cannot load unsigned modules.
Q8. What is the role of /lib/modules/$(uname -r)/modules.dep?
This file is a dependency map generated by depmod. It lists each module and the other modules it depends on. When you run modprobe my_module, the tool reads this file to determine which other modules to load first before loading the requested one. Without this file, modprobe cannot resolve dependencies.

Ready for the Next Step?

In Part 2, we write our very first kernel module from scratch — a Hello World LKM — and understand every single line of code.

Prev_Lec

Next_Lec

Leave a Reply

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