Table of Contents#
- What are Linux Loadable Kernel Modules?
- The Structure of a Loadable Kernel Module
- Compiling a Loadable Kernel Module
- Loading and Unloading a Kernel Module
- Module Parameters
- Interacting with the Kernel
- Common Practices and Best Practices
- Example Usage
- Conclusion
- References
1. What are Linux Loadable Kernel Modules?#
Linux Loadable Kernel Modules are pieces of compiled code that can be dynamically loaded into and unloaded from the running Linux kernel. They are a crucial part of the Linux kernel's modular architecture, which aims to keep the kernel core as small and efficient as possible. By using LKMs, the kernel can provide additional functionality only when it is needed, reducing memory usage and improving system performance.
LKMs are typically used for the following purposes:
- Device Drivers: To support new hardware devices without recompiling the entire kernel.
- Filesystem Drivers: To add support for new file systems.
- Networking Protocols: To implement custom networking protocols.
- Security Modules: To enhance the security of the system.
2. The Structure of a Loadable Kernel Module#
A basic Linux Loadable Kernel Module consists of the following components:
- Header Files: Include necessary kernel headers, such as
linux/init.handlinux/module.h. - Initialization Function: A function that is called when the module is loaded into the kernel. This function is usually named
init_moduleor has themodule_initmacro. - Cleanup Function: A function that is called when the module is unloaded from the kernel. This function is usually named
cleanup_moduleor has themodule_exitmacro. - Module Information: Metadata about the module, such as the author, description, and license.
Here is a simple example of a basic kernel module:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
// Initialization function
static int __init hello_init(void) {
printk(KERN_INFO "Hello, world!\n");
return 0;
}
// Cleanup function
static void __exit hello_exit(void) {
printk(KERN_INFO "Goodbye, world!\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple Hello World kernel module");In this example, the hello_init function is called when the module is loaded, and it prints a message to the kernel log. The hello_exit function is called when the module is unloaded, and it also prints a message to the kernel log. The MODULE_LICENSE macro specifies the license under which the module is released, and the MODULE_AUTHOR and MODULE_DESCRIPTION macros provide information about the module's author and description.
3. Compiling a Loadable Kernel Module#
To compile a Linux Loadable Kernel Module, you need to have the kernel headers installed on your system. The kernel headers contain the necessary definitions and declarations for kernel programming. Here are the steps to compile a kernel module:
Step 1: Create a Makefile#
A Makefile is a script that automates the compilation process. Here is a simple Makefile for compiling a kernel module:
obj-m += hello.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) cleanIn this Makefile, obj-m specifies the name of the module object file. The all target compiles the module, and the clean target removes the compiled files.
Step 2: Compile the Module#
To compile the module, simply run the following command in the terminal:
makeThis will generate a .ko file, which is the compiled kernel module.
4. Loading and Unloading a Kernel Module#
Once you have compiled the kernel module, you can load it into the running kernel using the insmod or modprobe command.
Loading a Module with insmod#
The insmod command is used to insert a module into the kernel. To load the hello.ko module, run the following command:
sudo insmod hello.koYou can check the kernel log to see if the module was loaded successfully:
dmesg | tailLoading a Module with modprobe#
The modprobe command is a more advanced tool for loading modules. It can automatically resolve module dependencies and load all the necessary modules. To load the hello module using modprobe, run the following command:
sudo modprobe helloUnloading a Module#
To unload a module from the kernel, use the rmmod command:
sudo rmmod helloAgain, you can check the kernel log to see if the module was unloaded successfully:
dmesg | tail5. Module Parameters#
Kernel modules can accept parameters when they are loaded. This allows you to customize the behavior of the module. To define a module parameter, use the module_param macro. Here is an example:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
static int my_param = 0;
module_param(my_param, int, 0644);
MODULE_PARM_DESC(my_param, "An integer parameter for the module");
static int __init param_init(void) {
printk(KERN_INFO "Module parameter value: %d\n", my_param);
return 0;
}
static void __exit param_exit(void) {
printk(KERN_INFO "Exiting module\n");
}
module_init(param_init);
module_exit(param_exit);
MODULE_LICENSE("GPL");In this example, the my_param variable is defined as a module parameter. The module_param macro takes three arguments: the name of the parameter, its type, and the file permissions for the corresponding /sys/module entry. The MODULE_PARM_DESC macro provides a description for the parameter.
To pass a value to the parameter when loading the module, use the following syntax:
sudo insmod param.ko my_param=426. Interacting with the Kernel#
Kernel modules can interact with the kernel in various ways, such as accessing kernel data structures, registering device drivers, and handling interrupts. Here are some common ways to interact with the kernel:
Accessing Kernel Data Structures#
Kernel modules can access kernel data structures, such as the process list, file system information, and network sockets. However, this should be done with caution, as incorrect access can lead to system crashes.
Registering Device Drivers#
To add support for a new hardware device, a module can register a device driver with the kernel. The kernel provides a set of functions and data structures for device driver development.
Handling Interrupts#
Kernel modules can handle interrupts generated by hardware devices. This allows the module to respond to events in a timely manner.
7. Common Practices and Best Practices#
Here are some common practices and best practices for developing Linux Loadable Kernel Modules:
Common Practices#
- Error Handling: Always handle errors properly in your module code. Return appropriate error codes from your initialization and cleanup functions.
- Memory Management: Use the kernel's memory management functions, such as
kmallocandkfree, to allocate and free memory. - Synchronization: Use kernel synchronization mechanisms, such as mutexes and spinlocks, to protect shared data.
Best Practices#
- Keep it Simple: Write modular and easy-to-understand code. Avoid complex logic in your kernel module.
- Test Thoroughly: Test your module on a test system before deploying it to a production environment.
- Follow the Kernel Coding Style: Follow the Linux kernel coding style guidelines to ensure your code is consistent with the kernel source code.
8. Example Usage#
Here is a more practical example of a kernel module that counts the number of times the character device file is opened:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
static int open_count = 0;
static int major = 0;
static struct file_operations my_fops = {
.open = my_open,
};
static int my_open(struct inode *inode, struct file *filp) {
open_count++;
printk(KERN_INFO "Device opened %d times\n", open_count);
return 0;
}
static int __init open_counter_init(void) {
major = register_chrdev(0, "open_counter", &my_fops);
return 0;
}
static void __exit open_counter_exit(void) {
unregister_chrdev(major, "open_counter");
}
module_init(open_counter_init);
module_exit(open_counter_exit);
MODULE_LICENSE("GPL");In this example, the module registers a character device driver and intercepts the open system call for that device file. Every time the character device file is opened, the open_count variable is incremented, and a message is printed to the kernel log. Note that this only tracks opens of the character device itself, not arbitrary files.
9. Conclusion#
Linux Loadable Kernel Modules are a powerful feature of the Linux kernel that allows you to extend the kernel's functionality without recompiling or rebooting the system. By understanding the structure, compilation, loading, and unloading processes of LKMs, as well as how to interact with the kernel and follow best practices, you can develop your own kernel modules to meet specific requirements.