Loadable kernel modules are pieces of code that can be loaded and unloaded into the kernel upon demand. They provide a way to extend the functionality of the kernel without having to rebuild or reboot the system. In this article, let’s understand these kernel modules better.
What is a loadable kernel module?
A loadable kernel module (LKM) is a piece of code that can be loaded and unloaded into the kernel upon demand. LKMs are typically used to add support for new hardware (as device drivers) or filesystems, or for adding system calls.
In Linux, the term “module” refers to a loadable kernel object. Modules can be statically compiled into the kernel or dynamically loaded at runtime. Dynamic loading is generally preferred, as it allows for more flexibility (modules can be loaded and unloaded as needed) and makes it easier to debug the kernel (if a module is causing problems, it can simply be removed without having to recompile the entire kernel).
The concept of loadable kernel modules was first introduced in Unix System V Release 4 (SVR4), where they were used primarily for device drivers. In Linux, the idea was adopted and expanded upon, with LKMs now used for a wide variety of purposes.
Advantages of Using Loadable Kernel Modules
There are several advantages to using LKMs:
- They allow for code reuse – if multiple devices need support for the same driver or filesystem, that code can be placed in a single module which can then be used by all of those devices. This reduces code duplication and clutter.
- They make it possible to add or remove functionality from the kernel without needing to recompile it – this makes debugging much easier, as problematic modules can simply be removed rather than having to recompile an entirely new kernel every time something needs to be changed or fixed.
- They improve security – since modules are not part of the main kernel image, they cannot directly attack other parts of the system if compromised. This isolation protects critical parts of the system from being attacked through vulnerabilities in less important components.
How Can Loadable Kernel Modules Be Used?
As mentioned above, LKMs can be used for a variety of purposes such as device drivers, filesystems, system calls, network drivers, TTY line instructions, useful interpreters etc. Some common uses are listed below:
Device Drivers
One of the most common uses for LKMs is writing device drivers. A device driver is a piece of software that allows your operating system to communicate with hardware devices such as printers or graphics cards. Without device drivers, your computer would not be able to use these devices properly (or at all).
Filesystem Drivers
Another common use for LKMs is writing filesystem drivers. A filesystem is what your operating system uses to store files on disk (e.g., ext3/4), and each type of filesystem has its own driver which tells the OS how to read/write data on that particular type of filesystem.
System Calls
System calls are how user-space programs interact with the kernel; they provide a way for programs to request services from the kernel (such as reading/writing files or creating processes). Many system calls are implemented via LKMs; adding new system calls typically requires creating a new LKM.
Network Drivers
Network drivers are another common use for LKMs. These days most computers have some form of network connection(Ethernet, WiFi, etc.), and each type of network has its own driver which handles the communication between the computer and the network. Adding new types of networks often requires creating new LKMs.
TTY Line Instructions
TTY (Teletype) line instructions are how user-space programs interact with serial devices such as modems or terminals. TTY line instructions are typically implemented via LKMs.
Useful Interpreters
Many interpreted languages (such as Perl, Python, and Ruby) can be used to write LKMs. This can be useful for writing system administration utilities or other tools that need to interact directly with the kernel.
Compiling Kernel Modules
In order to use loadable kernel modules, they must first be compiled. The process of compiling a module is similar to compiling a regular program; however, there are some important differences.
First, you will need a copy of the kernel source code. This is because the module needs to know about the internals of the kernel it will be interacting with; without access to the source code, it would not be possible to compile a working module. Fortunately, most major Linux distributions make the kernel source code available through their package repositories.
For example, on Debian – based systems (such as Ubuntu ), you can install the linux-source package; on Red Hat-based systems ( such as Fedora ), you can install the kernel-devel package. Once you have installed the appropriate source code package, you will find the kernel source in the /usr/src directory (on Debian-based systems) or /usr/share/doc/kernel-$version(on RedHat-based systems), where $ version is the version of your current kernel.
Second, you need to have a compiler toolchain installed so that you can compile your code into a binary object file (.o). On Debian-based systems, this can be done by installing build-essential; on Red Hat-based systems, GCC should already be installed by default. If not, it can be installed using yum install GCC.
Once you have a compiler toolchain set up, you should also ensure that you have made installed; this is generally available through your distribution’s package repository
apt install make
Fourthly, once your module is sourced and compiled successfully into an object file with no dependencies then it is ready to be inserted into the kernel to view if it works properly or not and what effect it has on the kernel space and other modules already running inside the kernel space before this new module was even thought of being introduced to user space or application space for that matter.
Example of a kernel module that passes parameters from userspace to kernel space
#include <linux/module.h>
#include <linux/kernel.h>
static int my_int = 0;
module_param(my_int, int, S_IRUSR | S_IWUSR);
MODULE_PARM_DESC(my_int, "An integer");
static char *mystring = "default";
module_param(mystring, charp, 0000);
MODULE_PARM (mystring , “s”) ;
/* same as above */
MODULE _ PARM _ DESC ( my string , “A character string” );
static int __init hello2 _ init ( void ) { printk("Hello world 2");
printk("My Int is %d and My String is %s", myInt, myString);
return 0; }
static void __exit hello2 _ exit ( void ) { printk("Goodbye world 2"); }
module _ init ( hello2 _ init );
module _ exit ( hello2 _ exit );
MODULE NAME ("hello-lkm-params") ;
AUTHOR ("Dee") ;
DESCRIPTION ("This is a simple example to show how to pass parameters from userspace to kernel space by insmod or modprobe!");
MODULE_LICENSE("GPL");
- The code above shows how to pass parameters from userspace to kernel space by using insmod or modprobe. The my_int and mystring variables are declared as global variables. The module_param() macro is used to specify the parameters that can be passed in from userspace. The S_IRUSR and S_IWUSR flags indicate that the parameter is readable and writable by user space. The MODULE _ PARM _ DESC macro is used to provide a description of the parameter for use by modinfo.
- The hello2 _ init () function is called when the module is loaded, and it prints a message to the kernel log indicating that the module has been loaded. It also prints the value of myInt and mystring.
- The hello2 _ exit () function is called when the module is unloaded, and it prints a message to the kernel log indicating that the module has been unloaded.
- The module_init() and module_exit() macros are used to specify the functions that should be called when the module is loaded and unloaded, respectively.
- The MODULE_NAME macro is used to provide a name for the module. The MODULE _ LICENSE macro is used to specify the license under which the module is released.
How to use loadable kernel modules in Linux?
There are two ways to load a module into the Linux kernel: using insmod or modprobe. Insmod is used for manually loading a single module, while modprobe can be used for both manual and automated loading/unloading of multiple modules.
To use either program, you must first have a compiled module (usually with a .ko suffix). For example, let’s say you have a module named my_module.ko. To load it using insmod, you would type:
sudo insmod my_module.ko
To unload it, you would type:
sudo rmmod my_module
If you want to automatically load your module when your system boots up, you can add an entry for it in /etc/modules:
my_module
This will cause modprobe to automatically load your module when the system boots up (or whenever modprobe is run with no arguments). You can also specify options that should be passed to your module by adding them after the module name, separated by spaces:
my_module option1=value1 option2=value2 ...
Conclusion
In conclusion, loadable kernel modules are a powerful and useful tool that can be used for a variety of purposes. They allow for code reuse, improve security, and make debugging easier. If you are developing software that needs to interact directly with the kernel, or if you are writing a device driver or filesystem driver, then you will likely need to use an LKM.