Getting started with Kernel Modules
Description
This is an intro to modern Linux kernel module programming as opposed to compiling an entire kernel. We will present the preliminary knowledge needed and the basic setup. You will learn how to identify your kernel version, be presented a starter code template, learn how to compile, and how to load your module.
Audience
You have basic C programming skills and are familiar with the Linux kernel.
Warning
Linux operating systems are secure by design. They are designed with a layer of separation between the software that needs direct access to hardware that is the Linux Kernel and all other software that is referred to "user" software. The kernel has it's own space on memory and cpu and user programs have their own space; kernel space is safely separated from user space. So with kernel programming you are directly operating on hardware and so you do not have that safety buffer layer of separation.
"A single wild pointer can wipe out your file system."
"A core dump means a reboot"
REF paragraph 1: tldp.org/LDP/lkmpg/2.6/html/x40.html
Identify your system and kernel versions
$ cat /etc/os-release
Debian GNU/Linux 12 (bookworm)
We are running the Debian 12 codenamed Bookworm which is the current stable version of Debian as of the date of this article Jan 2025; debian.org/releases.
$ uname -r
6.1.0-28-amd64
We are running Linux version 6.1. The most recent Linux version at kernel.org is 6.13 btw.
Install necessary tools
We will need the kernel headers to compile kernel modules.
$ apt search linux headers 6.1.0-28-amd64
linux-headers-6.1.0-28-amd64
Install it.
# apt install linux-headers-6.1.0-28-amd64
The following additional packages will be installed:
linux-compiler-gcc-12-x86
linux-headers-6.1.0-28-common
linux-kbuild-6.1
linux-libc-dev
Ahh, so kernels requires their own special gcc compiler and a special build tool called Kbuild.
Kernel Module Formatting Template
Here is the template for programming a kernel module. It just prints using printk which is a the kernel print function. It is just 14 lines.
REF tldp.org/LDP/lkmpg/2.6/html/hello2.html
File link: ./km-hw-01/km_hw_01.c
Makefile for above Module
Kernel modules are built using a special build tool called Kbuild. A special Makefile variable is availabe in make's extended syntax for building using Kbuild namely "obj-m". This variable sets the ".o" object file that is passed to Kbuild. Usually we call "gcc" in our Makefile but with kernel modules we call "make" in our Makefile. When we call make in our Makefile, we need to cd(change directory) into the Linux source tree build directory using the "-C". Next we need to assign our development module's path to the special Makefile variable "M=". Finally we must supply the keyword option "module".
REF tldp.org/LDP/lkmpg/2.6/html/x181.html
REF oreilly.com/library/view/linux-device-drivers/0596005903/ch02.html
File link: ./km-hw-01/Makefile
make kbuild it
As mentioned about, our Makefile has set the "M=" assignment to "$(PWD)" and so we must run make from the directory or our module program
$ ls
km_hw_01.c Makefile
$ make
make[1] ; CC [M] ; MODPOST ; CC [M] ; LD [M] ; BTF [M]
Skipping BTF generation
A number of files have been generated. We will load the ".ko" file. We can ignore that last warning regarding BTF.
Loading/Inserting into Kernel
# insmod km_hw_01.ko
If insmod returned without error we are good to go.
Viewing our Running Module
We can see our Kernel module running on our Linux system using lsmod.
# lsmod | grep km_hw_01
km_hw_01 16384 0
We can do the same as above by viewing the /proc/modules file; lsmod reads from it.
# cat /proc/modules | grep km_hw_01
km_hw_01 16384 0
Removing/unloaded the Module
# rmmod km_hw_01
Since km_hw_01 is loaded into the Kernel it is listed in the /proc/modules file and we can call rmmod from any path location where as we needed to be in the module development directory where the ".ko" file is located to insert it into the Kernel.
Viewing the printk()'s
Recall our module did not do anything except call the kernel print function printk(). In the recent past, Kernel logs used to be viewable at /var/log/messages. Since the Debian Bookwork update we now use journalctl.
REF debian.org/releases/bookworm/amd64/release-notes/ch-information.en.html#changes-to-system-logging
It is a long print out; pages upon pages going back for days. We can jump to the end using the "-e" option. Or we can us the "-f" option to see Kernel logs as they print out. So when you insmod, you should see the init print out, and when we rmmod the exit printout. You can also use dmesg.
insmod
rmmod
dmesg
Final Notes
Notice repeated init_km_hw_01() print outs in the above dmesg screenshot. Linux allows us to repeatedly load the same module. It does not return error such as "a module by the same name is already loaded" or similar. Also notice, if you were to run lsmod or cat /proc/modules you would see that only one init_km_hw_01() is loaded.
For a more detailed explanation of kbuild for building an out-of-tree kernel module see the following reference: kernel.org/doc/Documentation/kbuild/modules.txt




