Linux Kernel Programming Basics, Create Loadable Kernel Module

What is Linux Kernel?

Kernel is the core and central part of the Linux Operating system. It is responsible for the most critical functions of the operating system like process scheduling, memory management, file system management, device handling, networking, interrupt handing etc. It runs on a restricted part of the physical memory, known as Kernel Space, that can not be directly accessed by the user space programs. Kernel always runs as super user mode and has direct access to all hardwares and resources. It provides a set of functions and variables, also known as system calls or system APIs, to interact with the user space applications.

Why Kernel Programming?

Standard Kernel versions come with fundamental functionalities. But you might need to extend them for various reasons like you might add support for your new hardware or you might need to extend existing networking behavior. To do that, you have to add additional code to the existing kernel. What are the options? You have primarily two options.

  1. You can take the existing kernel code and add your code, then re-compile as monolithic kernel. You can use the newly created kernel that will serve your purpose.
  2. You can create loadable kernel module (KLM) that can be inserted into to kernel whenever required. This can be removed from the kernel if it is not required anymore.

Advantage of Loadable Kernel Module

Loadable Kernel Module has many advantages over monolithic kernel.

  1. You don’t need to compile the whole kernel. Compiling kernel requires some level of expertise.
  2. Loadable Kernel Module can be inserted into or removed from kernel anytime. If you add you code as monolithic kernel, the code will exist from boot-up and continue to remain in the kernel till shutdown.
  3. No need to restart the whole system. You can insert the module while operating system is up and running. In case monolithic kernel you need to change the kernel and restart the operating system.
  4. Easy to ship. If you want to use you module on other computers, you have to ship the module only. No need to change the whole kernel of the target computers.

Create your own module

Before starting to write the first kernel module, you have to make sure that the kernel development package is installed on your system. If you are using RedHat based Linux you can verify the using yum info kernel-devel command. If kernel-devel package is not available on your system then install it using yum install kernel-devel.
Now we’ll write the simplest kernel module my_module. Here is the code

Example 1:

#include <linux/module.h>       /* Needed by all modules */
#include <linux/kernel.h>       /* Needed for KERN_INFO */

int init_module(void)
{
        printk(KERN_INFO "Hello Kernel World!!!.n");
        /*
         * A non 0 return means init_module failed; module can't be loaded.
         */
        return 0;
}

void cleanup_module(void)
{
        printk(KERN_INFO "Goodbye Cruel World!!!n");
}

Write the code in my_module.c file. In your kernel module you need to have two functions. First one is init_module() which will be called when your module will be inserted into the kernel using insmod command. The name of the function must be init_module. In the next example, we’ll see how you can use a name of your choice for the initiation function. The purpose of this function is to add new code into the kernel or replace existing code. But in our example the init_module() function will simply print “Hello Kernel World!!!” in the system log. The next function is cleanup_module() which will be called when the module is removed from the kernel using rmmod. The purpose of this function is to cleanup the codes that were inserted by the init_module() function. As our init_module() function did not insert any code, we don’t have to do any cleanup. We’ll print another message “Goodbye Cruel World!!!” in the system log. We included two header files here. “module.h” is required for all kernel modules and “kernel.h” is required for the macro KERN_INFO used in the printk function. Another thing you have to remember, the init_module() function must return 0 for successful insertion of the module. Module will not be inserted into the kernel if it returns any value other than 0.

Example 2:

#include <linux/module.h>       /* Needed by all modules */
#include <linux/kernel.h>       /* Needed for KERN_INFO */
#include <linux/init.h>         /* Needed for the macros */

static int __init my_init(void)
{
        printk(KERN_INFO "Hello Kernel World!!!.n");
        return 0;
}

static void __exit my_exit(void)
{
        printk(KERN_INFO "Goodbye Cruel World!!!n");
}

module_init(my_init);
module_exit(my_exit);

Example 2 is same as example 1 except we used name our choice for the initiation (my_init) and cleanup (my_exit) functions in place of init_module() and cleanup_module(). For that we used two macros module_init and module_exit. For that we included an additional header file “init.h” where the macros are defined. On thing you have to make sure that the initiation and clean up functions need to be present before using the module_init and module_exit macros.

Now you have to compile you module. To do that you have to create a Makefile like this

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

To compile you have to run the make command in the directory where the Makefile and my_module.c file exist. Use example 2 in the my_module.c file.

# make
make -C /lib/modules/3.11.9-200.fc19.x86_64/build M=/root modules
make[1]: Entering directory `/usr/src/kernels/3.11.9-200.fc19.x86_64'
  CC [M]  /root/del/my_module.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /root/del/my_module.mod.o
  LD [M]  /root/del/my_module.ko
make[1]: Leaving directory `/usr/src/kernels/3.11.9-200.fc19.x86_64'

Read also: Compiling Linux Kernel Module

After successful compilation, you module my_module.ko will be created in the same directory. The module is now ready to be inserted into the kernel. Insert the module using insmod command.

# insmod my_module.ko

In time of insertion you my_init() function should be called. The the message in the function will be printed in the system log. You can verify using the following command.

# dmesg | tail -1
[ 2690.616865] Hello Kernel World!!!.

You can verify that your module is present in the kernel using lsmod command.

# lsmod
Module                  Size  Used by
my_module              12422  0
ebtable_nat            12807  0
nf_conntrack_netbios_ns    12665  0
nf_conntrack_broadcast    12527  1 nf_conntrack_netbios_ns
ipt_MASQUERADE         12880  1
ip6table_nat           13015  1
nf_nat_ipv6            13213  1 ip6table_nat
ip6table_mangle        12700  1
ip6t_REJECT            12939  2
nf_conntrack_ipv6      18738  24
nf_defrag_ipv6         34595  1 nf_conntrack_ipv6
iptable_nat            13011  1

You will see that your module is present in the Module list.

You can remove your module from kernel using rmmod command.

# rmmod my_module

Your cleanup function my_exit() should be called. You can verify that from the system log

# dmesg | tail -1
[ 2800.654088] Goodbye Cruel World!!!

If you use the lsmod command, you’ll see that your module is no longer available in the Module list.

Congratulations!!! You have created and made your first kernel module work.

Author: Srikanta

I write here to help the readers learn and understand computer programing, algorithms, networking, OS concepts etc. in a simple way. I have 20 years of working experience in computer networking and industrial automation.


If you also want to contribute, click here.

Leave a Reply

Your email address will not be published. Required fields are marked *

0
0
0
0
0
1