Kernel Modules (Load/Unload)
Embedded → Embedded Linux: Kernel & Drivers (advanced track)
Code that joins the kernel without a reboot
Every device driver you'll write in this topic eventually ships as a loadable kernel module (LKM) — a .kofile linked into the already-running kernel's address space on demand, and unlinked again when done. This is the entire reason kernel development is iterable: without modules, testing a one-line driver change would mean rebuilding the whole kernel image and rebooting every time. Everything else in this topic (char devices, device-tree binding, sysfs attributes) is written inside a module.
The two functions every module must define
A module has no main(). Instead it registers one function to run the instant it's loaded, one to run the instant it's unloaded — everything a module does happens inside those two entry points:
// hello.c — the minimal loadable kernel module
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("you");
MODULE_DESCRIPTION("A minimal example kernel module");
static int __init hello_init(void)
{
printk(KERN_INFO "hello: module loaded\n");
return 0; // non-zero return = load FAILS, init never registered
}
static void __exit hello_exit(void)
{
printk(KERN_INFO "hello: module unloaded\n");
}
module_init(hello_init);
module_exit(hello_exit);printk is printf for kernel code — a module has no stdout of its own. Every printk call gets timestamped into the kernel's ring buffer, read back with dmesg. KERN_INFO is a log-level prefix (also KERN_ERR, KERN_WARNING, ...) controlling whether a message also mirrors to the console in real time, separately from always landing in the ring buffer.Building it — a two-line Makefile, not a normal gcc invocation
A module isn't compiled with a plain gcccall — it has to be built against the exact kernel headers/build system of the kernel it will load into, using the kernel's own Makefile infrastructure:
# Makefile 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 $ make CC [M] hello.o MODPOST hello.mod.o CC [M] hello.mod.o LD [M] hello.ko
insmod / modprobe, lsmod, dmesg, rmmod — the full command loop
The video's own command sequence, exactly as you'd run it after a successful build:
$ sudo insmod hello.ko # load it — insmod does NO dependency resolution $ dmesg | tail -1 [12345.678] hello: module loaded $ lsmod | grep hello # confirm it's resident, check its refcount (last column) hello 16384 0 $ modinfo hello.ko # inspect metadata WITHOUT loading it filename: /home/you/hello.ko license: GPL description: A minimal example kernel module $ sudo rmmod hello # unload it $ dmesg | tail -1 [12346.901] hello: module unloaded
insmod loads exactly the .ko file you point it at and does nothing else — if that module depends on symbols exported by another module not already loaded, it fails. modprobe is the dependency-aware sibling: it reads modules.dep (built by depmod) and loads prerequisite modules first, by name, out of the standard /lib/modules/$(uname -r)/ tree. For a single self-contained module during development, insmod is simpler; production systems almost always use modprobe.rmmod refuses to unload a module whose refcount (that last lsmod column) is nonzero — something else in the kernel is still using it, e.g. a char device this module registered still has a userspace process holding it open. Forcing an unload of code something else is using is exactly the kind of use-after-free that crashes the whole kernel, not just one process.Try it — step through the module lifecycle
Walk insmod → lsmod → rmmod and watch the module's live state plus the dmesg ring buffer update at each step:
$ lsmod | grep hello
(empty)
Nothing to find yet — the module hasn't been loaded.
Practice
hello.c module above on a real Linux VM (or WSL2, or a board), confirm insmod then dmesg shows your load message, run lsmod and modinfo against it, then rmmod it and confirm the unload message. Then add a module parameter (module_param) and pass a different value at insmod time to see it change the printed message.Completion saves automatically after you read through to the end.
Next lesson →