How to Write a Simple Linux Kernel Module?

adil
3 min readMar 9, 2024

--

Kernel module development can be challenging. Let’s write our first simple Linux Kernel Module.

Photo by Filip Zrnzević on Unsplash

Let’s create a directory:

mkdir blog-hello-world-kernel

Place a Makefile in that directory:

obj-m += bloghello.o
PWD := $(CURDIR)
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

Place a simple C file:

bloghello.c

#include <linux/init.h>
#include <linux/module.h>

static int __init construct(void) {
pr_info("First kernel module: Hello World!\n");
return 0;
}

static void __exit destruct(void) {
pr_info("First kernel module has been removed\n");
}

module_init(construct);
module_exit(destruct);

MODULE_LICENSE("GPL");

Let’s build it:

root@main:~/blog-hello-world-kernel# make
make -C /lib/modules/5.11.0-49-generic/build M=/root/blog-hello-world-kernel modules
make[1]: Entering directory '/usr/src/linux-headers-5.11.0-49-generic'
CC [M] /root/blog-hello-world-kernel/bloghello.o
MODPOST /root/blog-hello-world-kernel/Module.symvers
CC [M] /root/blog-hello-world-kernel/bloghello.mod.o
LD [M] /root/blog-hello-world-kernel/bloghello.ko
BTF [M] /root/blog-hello-world-kernel/bloghello.ko
Skipping BTF generation for /root/blog-hello-world-kernel/bloghello.ko due to unavailability of vmlinux
make[1]: Leaving directory '/usr/src/linux-headers-5.11.0-49-generic'
root@main:~/blog-hello-world-kernel#

The loadable kernel object has been created:

You can install the Kernel Object via the insmod command:

insmod bloghello.ko

You can check if it is actually installed via lsmod:

You can see the module’s output in /var/log/syslog or in the output of dmesg :

You can uninstall the module via rmmod :

rmmod bloghello

Let’s check dmesg once more:

Congratulations! You’ve managed to construct your first Linux Kernel module.

You can print messages to different log levels:

#include <linux/init.h>
#include <linux/module.h>

static int __init construct(void) {
printk(KERN_INFO "INFO Level message\n");
pr_info("Another INFO Level message\n");

printk(KERN_WARNING "WARNING Level message\n");
pr_warn("Another WARNING Level message\n");

printk(KERN_ERR "ERROR Level message\n");
pr_err("Another ERROR Level message\n");

printk(KERN_CONT "This " );
pr_cont("message ");
printk(KERN_CONT "is " );
pr_cont("single ");
printk(KERN_CONT "line\n");

return 0;
}

static void __exit destruct(void) {
printk(KERN_INFO "The module has been removed\n");
}

module_init(construct);
module_exit(destruct);

MODULE_LICENSE("GPL");

Let’s check dmesg :

You can filter log lines by levels:

--

--