Writing a Char Module
The Code
Writing a Char Module is suprisingly simple. First, we specify what happens on init
(loading of the module) and exit
(unloading of the module). We need some special headers for this.
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("Mine!");
static int intro_init(void) {
printk(KERN_ALERT "Custom Module Started!\n");
return 0;
}
static void intro_exit(void) {
printk(KERN_ALERT "Custom Module Stopped :(\n");
}
module_init(intro_init);
module_exit(intro_exit);
It looks simple, because it is simple. For now, anyway.
First we set the license, because otherwise we get a warning, and I hate warnings. Next we tell the module what to do on load (intro_init()
) and unload (intro_exit()
). Note we put parameters as void
, this is because kernel modules are very picky about requiring parameters (even if just void).
We then register the purposes of the functions using module_init()
and module_exit()
.
Note that we use printk
rather than printf
. GLIBC doesn't exist in kernel mode, and instead we use C's in-built kernel functionality. KERN_ALERT
is specifies the type of message sent, and there are many more types.
Compiling
Compiling a Kernel Object can seem a little more complex as we use a Makefile
, but it's surprisingly simple:
obj-m += intro.o
all:
$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
$(MAKE)
is a special flag that effectively calls make
, but it propagate all same flags that our Makefile
was called with. So, for example, if we call
$ make -j 8
Then $(MAKE)
will become make -j 8
. Essentially, $(MAKE)
is make
, which compiles the module. The files produced are defined at the top as obj-m
. Note that compilation is unique per kernel, which is why the compiling process uses your unique kernel build section.
Using the Kernel Module
Now we've got a ko
file compiled, we can add it to the list of active modules:
$ sudo insmod test.ko
If it's successful, there will be no response. But where did it print to?
Remember, the kernel program has no concept of userspace; it does not know you ran it, nor does it bother communicating with userspace. Instead, this code runs in the kernel, and we can check the output using sudo dmesg
.
$ sudo dmesg | tail -n 1
[ 3645.657331] Custom Module Started!
Here we grab the last line using tail
- as you can see, our printk
is called!
Now let's unload the module:
$ sudo rmmod test
$ sudo dmesg | tail -n 1
[ 4046.904898] Custom Module Stopped :(
And there our intro_exit
is called.
Last updated
Was this helpful?