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.
|
USER SPACE Applications | Shell | Libraries (glibc) |
|||||||||||||||
| ⬆ System Call Interface (read, write, open, ioctl …) ⬇ | |||||||||||||||
|
KERNEL SPACE
|
|||||||||||||||
| ⚙ Hardware — CPU | RAM | Storage | Network Interface | Peripherals | |||||||||||||||
2. Static vs Dynamic Kernel Modules
When you build the Linux kernel, every feature you enable gets compiled in one of two ways:
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
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
| 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:
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.
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.
Add support for network protocols or packet filtering rules. Netfilter (the engine behind iptables and nftables) is loaded as kernel modules.
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.
|
📄 mydriver.c Source code written by you |
→ |
⚙ make Kernel build system (kbuild) |
→ |
📦 mydriver.ko Kernel Object binary |
|||||
|
|||||||||
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 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.
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:
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)
# Ubuntu / Debian
sudo apt install build-essential
# Fedora
sudo dnf groupinstall "Development Tools"
# Check kernel version
uname -r
# Confirm headers are present
ls /lib/modules/$(uname -r)/build
# Check gcc
gcc --version
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
| 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
✅ 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
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 ModuleEmbeddedPathashala — Free Linux Kernel Programming Course | Updated for Linux 6.x
