Keywords:
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.
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.
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/.├── 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.
.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.
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.
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.
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)
| 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.
- 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
- High-level, smart tool
- Just use the module name
- Automatically resolves dependencies
- Reads
/etc/modprobe.d/config files - Preferred for production use
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:
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.
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.
.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..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.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.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.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.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.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.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.