Introduction to Linux Kernel Modules
Linux operates in two modes. One is Kernel mode (kernel space) and other is User mode (user space). The kernel works in the highest level (also called as supervisor mode) where it has all the authority and the applications work in the lowest level where the direct access to the hardware and the memory are prohibited. Keeping in line with traditional Unix philosophy, Linux transfers the execution from user space to the kernel space through system calls and the hardware interrupts.
The Kernel code executing the system call works in the context of the process which has invoked the system call. As it operates on behalf of the calling process, it can access the data in the processes address space. The kernel code that handles interrupts, works to the processes and related to any particular process. The Linux kernel is a monolithic kernel; i.e. it is one single large program where all the functional components of the kernel have access to all of it’s internal data structures and routines.
The alternative to this is the micro kernel structure where the functional pieces of the kernel is broken out into units with strict communication mechanism between them. This makes adding new components into the kernel via the configuration process rather time consuming. The best and the robust alternative is the ability to dynamically load and unload the components of the operating system using Linux Kernel Modules. The linux kernel modules are the piece of codes which can be dynamically linked to the kernel (according to the need) even after the system bootup.
They can be unlinked from the kernel and removed when they are no longer needed. Mostly the linux kernel modules are used for the device drivers or the pseudo-device drivers such as network drivers or file system. When a linux kernel module is loaded, it becomes the part of the Linux kernel as the normal kernel code and functionality and it posses the same rights and responsibilities as the kernel code. Life Cycle of Linux Kernel Module:Let us now discuss about the life cycle of the Linux Kernel Module. The life cycle of a module starts with the init_module(). The task of init_module is to prepare the module for the later invocation.
The module is registered to the kernel and attaches it’s data-structures and functionality in the kernel. The kernel defined external functions are also resolved. The life-cycle of the module ends with cleanup_module() . It unregisters the module functionality from the kernel. We will go into the detailed life cycle of the module later. Simple Module Program: Let us now program a simple module to review its life-cycle. In the following program, the init_module and the cleanup_module functions are demonstrated. The init_module is called when the module is inserted into the kernel and the cleanup_module is called just before removing it from the kernel.
/* Simple Linux Kernel Module feb’2000 archanp@bigfoot.com */
#include
#include
#if CONFIG_MODVERSIONS==1
#define MODVERSINS
#include
#endif
/* initialise the module */
int init_module()
{
printk(”init_module invoked\n”);
printk(”the message is printed from the kernel space\n”);
/* if the non zero value is returned, then it is
meant that the init_module failed and the kernel module can’t
be loaded */
return 0;
}
/* cleanup / end of module life cycle */
void cleanup_module()
{
printk(”cleanup_module invoked\n”);
printk(”module is now going to be
unloaded from the kernel\n”);
}
Compile the above program using the following :
#gcc -Wall -DMODULE -D__KERNEL__ -DLINUX -O -c simpelModule.c
Run the compiled module using the following :
#insmod simpleModule.o
Remember, you have to run the above command from the Linux shell at raw console (not from the console in Xwindows environment) at root login. Then check the status of the module using the following.
#lsmod
Then remove the module using the following
#rmmod simpleModule
If you have not seen any of the module initiated console printing (implemented using printk) about the status of the module, use the following command to see the kernel messages.
dmesg | less
In the above, there commands (insmod, lsmod and rmmod ) are used to load and unload modules to the Linux kernel. The details are discussed in the following section. Loading Modules:insmod loads the loadable kernel modules in the running kernel. insmod tries to link a module into the running kernel by resolving all the symbols from the kernel’s exported symbol table. Now we will discuss about the demand loading of the module by the kernel (dynamically). When the linux kernel discovers the need for a module, the kernel request to the kernel daemon ( kerneld) to load the appropriate module. Let’s take an example: we are going to mount a NTFS partition in the Linux system. If the NTFS filesystem support is not statically implemented in the kernel (but compiled as a module), the kernel daemon will search for the appropriate module and load it from the repository. Then the parition is mounted for the use. Let’s go into deep of the action of the kernel daemon (kerneld). The kerneld is the normal user process having the th exclusive superuser privileges. At the time of the booting, kerneld open the IPC channel to the kernel and uses it for transfering messages (request for loading modules) to and from the kernel. While loading the loading the module, the kerneld calls modprobe and insmod to load the required module. The insmod utility should able to access the requested module. The demand loadable kernel modules are usually located at the /lib/module/ directory as the object files linked as relocatable images . Let us now revisit the working of the insmod to get the clear picture of the module loading operation. We have already seen that the insmod is used to laod module to the kernel. The insmod depends on some the critical system calls to load the module to the kernel. The insmod uses the sys_create_module to allocate kernel memory to hold module. insmod uses get_kernel_syms system call to get the kernel symbol table inorder to link the module. Then it calls sys_init_module system call to copy the relocatable object code of the module to the kernel space. And soon after this, insmod calls the initialization function of the concerned module i.e. init_module . All of these system calls can be found in kernel/module.c. Unloading Modules:The modules can be unloaded using rmmod command. While removing modules, rmmod ensures the restriction that the modules are not in use and they are not referred by any other module or the part of the kernel. The demand loaded modules (ie. the modules loaded by kerneld) are automatically removed from the system by kerneld when they are no longer used. Every time it’s idle timer expires, kerneld makes a system call requesting for all the demand loaded kernels which are not busy state should be removed. The modules whose visited flags are cleared and marked as AUTOCLEAN , are unloaded. Asuming that the module can be unloaded, the cleanup_module function of the concerned module is called to freeup the kernel resources it has allocated. After the successful execution of the cleanup_module, the module datastructure is marked DELETED and it is unlinked from the kernel and is unlisted from the list of kernel modules. All the reference list of the modules on which it (module removed) is dependent are mofied and dependency is released. All the kernel momory allocated to the concerned module are deallocated and returned to the kernel memory spool. Version Dependency of Modules: The version dependency of the module is one of the most tricky part of the Linux Kernel Module programming. Typically, the modules are requred to be compiled for each version of the kernel. Each module defines a symbol called kernel_version, which insmod matches against the version number of the current kernel. The current kernel 2.2.x/2.4.x define the symbol in . Hence if the module is made up of multiple source files, the should be included only one of the source files. Though typically, modules should be recompiled for each kernel version, it is not always possible to recompile module when it is run on as a commercial module distributed in binary form. The kernel developers has provided a flexible way to deal with the version problem. The idea is that a module is incompatible with a different kernel version, only if the software interface offered by the kernel is changed. The software interface is represented by the function prototype and the exact definition of all the data structures involved in the function call. The CRC algorithm can be used to map all the information about software interface to the single 32bit number. The issue of version dependency is handled by using the name of the each symbol exported. ConclusionIn the above sections, I have discussed the simple most life cycle of the module. For going deeper, please refer to the book by Rubini which I have mentioned in the begining of this article. It is also nice to look at the kernel code to refer to the system-calls which are responsible for module loading or unloading. I do hope that I will cover some more on Module-Programming in the future articles (provided, I have the time to do some writting in the wee hours).