In this tutorial you will learn how to code a simple and complete linux kernel module that will print "hello world" on loading and "Good Bye World" on unloading. Remember, this module is written for Kernel V.2.6.xx. So there's no guarantee that this module will compile/run in any of the previous versions.
Any Linux Kernel Module requires one entry function and one exit function. Traditionally these entry and exit
functions are "init_module()" and "cleanup_module()" respectively. However in kernel v2.6 , programmer has the
liberty to choose the name they want for the entry and exit functions with the help of kernel macros. The
prototype for the entry and exit functions init_module() and cleanup_module() is found in
#include <linux/module.h> /* needed by all modules */
int init_module()
{
return 0; /* return zero on successful loading */
}
void cleanup_module()
{
}
|
Or, with the custom entry and exit function names, the program will look like:
#include <linux/module.h> /* needed by all modules */
#include <linux/init.h> /* needed for macros */
int my_entry_fn_name()
{
return 0; /* return zero on successful loading */
}
void my_exit_fn_name()
{
}
module_init(my_entry_fn_name);
modue_exit(my_exit_fn_name);
|
One of the main purpose of the entry function init_module() is to register the module in the kernel as a handler for any feature or function that the module will be handling. init_module() returns an int and the return value is zero on a successful module load. In case of an error, the return value is -1 with errno set with any of the following values:
The next improvement over our code will be to add a print statement on both the entry and exit functions. In
Linux kernel, printing can be accomplished by using printk() function call. Unlike printf(), in printk() a
programmer can specify the severity of the message that will be printed. Remember, though printf() and
printk() accomplish similar tasks, the main objective of printk() is log a message as opposed to print a
message on the screen. Different levels of printk() messages are defined in
#define KERN_EMERG "<0>" /* system is unusable */ #define KERN_ALERT "<1>" /* action must be taken immediately */ #define KERN_CRIT "<2>" /* critical conditions */ #define KERN_ERR "<3>" /* error conditions */ #define KERN_WARNING "<4>" /* warning conditions */ #define KERN_NOTICE "<5>" /* normal but significant condition */ #define KERN_INFO "<6>" /* informational */ #define KERN_DEBUG "<7>" /* debug-level messages */
Now our improved module looks like :
#include <linux/module.h> /* needed by all modules */
#include <linux/init.h> /* needed for macros */
#include <linux/kernel.h> /* needed for printk */
int my_entry_fn_name()
{
printk(KERN_INFO "Hello World \n");
return 0; /* return zero on successful loading*/
}
void my_exit_fn_name()
{
printk(KERN_INFO "Good Bye World \n");
}
module_init(my_entry_fn_name);
modue_exit(my_exit_fn_name);
|
Remember, you can view the messages from printk() on the screen only if you are in console mode, in other words, you must not be in KDE or GNOME or any other desktop environment. Messages from printk() are logged in /var/log/messages. If you are running the program in a terminal from a desktop environment, you can view the messages by typing "tail /var/log/messages". Using "tail" command with option –f, will print the log messages in the screen whenever a new log is added to /var/log/messages.
Now, let's assume you wrote a module that you want to distribute under GPL license or you want to keep the module proprietary. To convey the licensing information and other pertaining information about your module to the end users who will be using your module, kernel v2.6.xxx provides certain macros. They are all defined in /linux/module.h and few of them are as follows :
MODULE_LICENSE() MODULE_AUTHOR() MODULE_DESCRIPTION()
Though adding these information is not mandatory, kernel will print a warning message about "tainted kernel" while loading a module with no licensing information. If you want to get rid of that warning message, you must use MODULE_LICENSE() macro. So the improved code with licensing and other module information will look like:
#include <linux/module.h> /* needed by all modules */
#include <linux/init.h> /* needed for macros */
#include <linux/kernel.h> /* needed for printk */
MODULE_LICENSE("GPL");
MODULE_AUTHOR(" Prabu Surendra, [surendra_prabu@emc.com]");
MODULE_DESCRIPTION("Hello World Module");
int my_entry_fn_name()
{
printk(KERN_INFO "Hello World \n");
return 0; // return zero on successful loading
}
void my_exit_fn_name()
{
printk(KERN_INFO "Good Bye World \n");
}
module_init(my_entry_fn_name);
modue_exit(my_exit_fn_name);
|
Yay! We are done with our first hello world module. Now compiling kernel module is entirely different from compiling other user space programs. Only line that is needed in a Makefile to compile this kernel module in kernel v2.6.xx is
obj-m := hello.o
and then running the following command will create a loadable module hello.ko
make -C /kernel_src_path SUBDIRS=/module_path |
where /kernel_src_path is the path of the kernel source against which your module will be compiled and /module_path is the path of your module. If you want to add your module as an embedded module in the kernel, replace obj-m with obj-y. To make our life easy, we can add all the info about compiling this kernel module in the Makefile itself. Improved Makefile will look like :
obj-m := hello.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
make -C $(KDIR) SUBDIRS=$(PWD) modules
|
Now do a make on the terminal and you will see some messages similar to the follwing:
BDIRS=/home/tutorials/helloworld/ make: Entering directory `/usr/src/kernels/2.6.18-1.2798.fc6-i586' CC [M] /home/tutorials/helloworld/hello.o Building modules, stage 2. MODPOST CC /home/tutorials/helloworld/hello.mod.o LD [M] /home/tutorials/helloworld/hello.ko make: Leaving directory `/usr/src/kernels/2.6.18-1.2798.fc6-i586' |
Upon successful compilation, module hello.ko will be created. Now before inserting your module, run tail command against /var/log/messages if you are running from a desktop environment (if you running from console, you don't need to run tail).
[root@tusky helloworld]# tail -f /var/log/messages |
Now run the following command in another terminal to insert your module:
[root@tusky helloworld]# insmod hello.ko |
By running lsmod you can see your module listed if it's loaded successfully.Now remove your module as follows:
[root@tusky helloworld]# rmmod hello.ko |
You should see the messages that we printed in entry and exit functions:
[root@tusky helloworld]# tail -f /var/log/messages Dec 1 16:16:44 localhost kernel: Hello World Dec 1 16:17:01 localhost kernel: Good Bye World |