Intention
Recently, i thought to learn about linux device drivers and started reading LDD, so I will be summarizing my learnings here. So, Simple step towards linux device driver is writing a simple module. I am using ubuntu machine and will be doing experiments on that.
1. Writing a Simple Loadable Module
Basically, a module:
- Registers an entry function (called at load time)
- Registers an exit function (called at unload time)
Here is the minimal, correct “Hello World” module:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
MODULE_LICENSE("GPL"); // General Public License
MODULE_AUTHOR("jdecodes"); // author name
MODULE_DESCRIPTION("Minimal jdecodes kernel module"); // module description
// initialization function
// __init macro comes from linux/init.h
static int __init jdecodes_init(void)
{
pr_info("jdecodes_init: loaded into kernel\n");
for (int i = 0; i < 5; i++) {
pr_info("jdecodes_init: Hello, jdecodes! Count: %d\n", i);
}
return 0;
}
static void __exit jdecodes_exit(void)
{
pr_info("jdecodes_exit: removed from kernel\n");
}
// register the module
module_init(jdecodes_init);
// unregister the module
module_exit(jdecodes_exit);
Key points
module_init()tells the kernel where execution beginsmodule_exit()tells the kernel how to clean uppr_info()logs messages to the kernel log (dmesg)__initand__exithelp the kernel manage memory efficiently
2. Writing the Makefile
Kernel modules must be built using the kernel build system. Create a file “MakeFile“make -C $KDIR M=$PWD [Targets]
obj-m := hello.o
# given compiling against host machine
# so, need to compile aganist correct version
# which we can get via "uname -r"
# to read that do " $ (shell uname -r)"
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean
What this does
obj-y → built-in (static module along with kernel)obj-m → moduleobj-n → disabled
obj-mdeclares a loadable module- The kernel’s own Makefile handles compilation and linking
- The result is a
.ko(kernel object) file
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
1 question which can arise is how make can build hello.ko, as we did not define any modules. well, that is already pass to obj-m as hello.ko, so kernel build system knows the exact path where it is build and module it needs to build. I will write a detailed article on Make system.
3. Compiling the Module
Run:
make
This produces several files, the most important being:
hello.ko— the actual kernel modulehello.o— compiled objectModule.symvers— symbol versioning metadata
Only hello.ko is loaded into the kernel.
4. Loading the Module into the Kernel
Loading a module requires root privileges:
sudo insmod hello.ko
The command itself produces no output.
To confirm execution, check the kernel log:
dmesg | tail
You should see:
[24942.100132] hello: loading out-of-tree module taints kernel.
[24942.100132 hello: module verification failed: signature and/or
[24942.100132] jdecodes_init: loaded into kernel
[24942.100136] jdecodes_init: Hello, jdecodes! Count: 0
[24942.100138] jdecodes_init: Hello, jdecodes! Count: 1
[24942.100139] jdecodes_init: Hello, jdecodes! Count: 2
[24942.100140] jdecodes_init: Hello, jdecodes! Count: 3
[24942.100141] jdecodes_init: Hello, jdecodes! Count: 4
You can also verify that the module is present:
lsmod | grep hello
5. Removing the Module
To unload the module:
sudo rmmod hello
dmesg | tail
[25400.539604] jdecodes_exit: removed from kernel
If removal fails, it usually means the module did not clean up correctly—something the kernel takes very seriously. atleast, writing a simple module, looks simple, we will see how complex the drivers become once we move further..
Read next article : https://jdecodes.wordpress.com/2026/01/13/linux-device-drivers-101-002-writing-pseudo-char-driver/
Leave a comment