First linux kernel module

Writing your first linux kernel module helps in understanding the basic requirements for developing kernel code, compiling, and comprehending its code flow. About 95% of the Linux kernel code is written in the C programming language. If you are familiar with C programming, understanding the Linux kernel becomes easier. However, there are a few things to keep in mind when dealing with kernel modules, which differentiate them from regular C applications.

In C, the code flow is linear; it starts with the main() function and ends when main() completes its task. You can easily follow the code, starting from main(). On the other hand, kernel modules are different. They are part of the operating system and cannot run independently. They follow instructions from user space or other kernel modules. The flow in kernel modules is purely nonlinear. Navigating through a kernel module might be challenging for beginners, but this series of articles will help you grasp Linux kernel internals with ease.

In the world of Linux, when you compile a C program, you get a binary file, often named ‘a.out’ by default. You can give it any name you like, though. To run this file, you simply use the ‘./a.out’ command, which executes your C code until it finishes its task.

kernel module

Now, when it comes to kernel modules, things are a bit different. Compiling a kernel module results in a binary file with a ‘.ko’ extension. Unlike C programs, you can’t run kernel modules directly. Instead, they need to be inserted into the Linux kernel’s core using the ‘insmod filename.ko’ command. Once inserted, your kernel module becomes an integral part of the operating system. This integration is powerful but also means that any bug in your kernel module could potentially lead to a system crash. The module persists even after a system reboot until you specifically remove it using ‘rmmod filename.ko’.

#include <linux/kernel.h>
#include <linux/module.h>
                                                                                                                      
static int __init my_module_init(void)
{
        printk("my first kernel module inserted);
        return 0;
}

static void __exit my_module_exit(void)
{
        printk("my first kernel module removed");
}

module_init(my_module_init);
module_exit(my_module_exit);


MODULE_LICENSE("GPL");

Kernel modules execute specific functions based on external instructions. For instance, when you use ‘insmod,’ it executes the module_init, which initiates my_module_init function. However, the module_exit will not execute unless ‘rmmod’ is issued. This is a fundamental concept, and as you delve into subsequent chapters, you’ll gain a deeper understanding of the mechanisms governing kernel code execution.

Before executing lets see how to compile a kernel module. For this we need a Makefile. This is used for compiling and cleaning kernel module. create a file with name ‘Makefile’ no extensions required.

obj-m := first-module.o                                                                                               

default:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

To execute the above Makefile, use the command make

$ make

make -C /lib/modules/5.15.0-86-generic/build M=/home/testVM/kmodules/first_module modules
make[1]: Entering directory '/usr/src/linux-headers-5.15.0-86-generic'
  CC [M]  /home/testVM/kmodules/first_module/first-module.o
  MODPOST /home/testVM/kmodules/first_module/Module.symvers
  CC [M]  /home/testVM/kmodules/first_module/first-module.mod.o
  LD [M]  /home/testVM/kmodules/first_module/first-module.ko
  BTF [M] /home/testVM/kmodules/first_module/first-module.ko
Skipping BTF generation for /home/testVM/kmodules/first_module/first-module.ko due to unavailability of vmlinux
make[1]: Leaving directory '/usr/src/linux-headers-5.15.0-86-generic'

This shows your kernel module compiled without any errors. Now you can see that there is .ko file in your current directory and this is the binary file of our kernel module.

In the above discussion. We have seen that insmod inserts kernel module and executes module_init function. As per our program when we insert our module we should get print statemtent as

my first kernel module inserted

But this wont happen because kernel logs are not visible like printf statements. For this we have to use command dmesg | tail -5. This shows the last 5 logs of kernel modules(not only your module but entire kernel logs). Or open a new terminal and type dmesg -w. It continuously monitors and displays kernel logs, making it useful for tracking.

By now we are ready with binaries i.e .ko file . Learned how to see kernel logs, to insert a kernel module we use insmod command, and to remove we use rmmod command. But here there is an issue. As kernel modules are privileged we cannot directly insert . For this we need sudo permissions. If you are a root user, you know sudo permissions you can proceed inserting a kernel module by the command.

$ sudo insmod mykmodule.ko

Remove the module by,

$ sudo rmmod mykmodule.ko.

Check in dmesg log you can see that your logs are visible, which shows successful insertion of inserting and removing kernel module.