Linux Kernel Modules
In the design of the Linux system kernel, kernel modules are an important mechanism for extending kernel functionality. They allow for the dynamic loading and unloading of code without the need to recompile the entire kernel. This not only enhances the system’s flexibility and maintainability but also provides developers with an efficient way to develop drivers. According to data from the Linux Kernel Archives, over 70% of the code in the Linux kernel exists in module form, widely used in device drivers, network protocols, and file system extensions. Understanding and mastering the principles and practices of kernel modules is crucial for system administrators, kernel developers, and embedded engineers. It helps optimize system performance, quickly fix issues, and extend functionality.
1. Basic Knowledge of Linux Kernel Modules
1.1 What is a Kernel Module?
A kernel module is a loadable code segment of the Linux kernel that can be dynamically inserted or removed from the kernel at runtime. It extends the functionality of the kernel without requiring a system reboot or kernel recompilation. Kernel modules typically exist in the form of <span>.ko</span> files, which include drivers, file systems, or network modules.
Core Features:
- Dynamic Loading: Loaded using
<span>insmod</span>, unloaded using<span>rmmod</span>. - Modularity: The kernel core is compact, with functionality extended through modules.
- Dependency Management: Modules can depend on other modules, automatically handled using
<span>modprobe</span>. - Security: Modules run in kernel space and require root permissions.
The invention of kernel modules stems from the modular design of the Linux kernel, introduced by Linus Torvalds in version 1.2. It addresses the issue of kernel bloat, making Linux more flexible.
1.2 Advantages of Kernel Modules
- Flexibility: Hot-pluggable modules without rebooting.
- Performance: Loaded on demand, saving memory.
- Maintainability: Modules can be developed and updated independently.
- Compatibility: Supports third-party drivers.
- Security: Module signatures prevent malicious loading.
For example, adding a new network card driver on a server only requires loading the module without downtime.
1.3 Types of Kernel Modules
- Device Drivers: Such as network cards (e1000), disks (sd_mod).
- File Systems: Such as ext4, ntfs.
- Network Protocols: Such as ipvs, nft.
- System Extensions: Such as cgroup, selinux.
View Modules:
lsmod
Example Output:
Module Size Used by
btrfs 1474560 0
zstd_compress 163840 1 btrfs
xor 24576 1 btrfs
zstd_decompress 81920 1 zstd_compress
raid6_pq 114688 1 btrfs
1.4 Lifecycle of Kernel Modules
The lifecycle of a module includes loading, initialization, usage, and unloading:
- Loading: insmod or modprobe.
- Initialization: module_init() function.
- Unloading: module_exit() function.
Example: Module Code:
#include <linux/module.h>
#include <linux/init.h>
static int __init my_module_init(void) {
printk(KERN_INFO "Module loaded\n");
return 0;
}
static void __exit my_module_exit(void) {
printk(KERN_INFO "Module unloaded\n");
}
module_init(my_module_init);
module_exit(my_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Author");
MODULE_DESCRIPTION("Simple module");
1.5 Management Goals of Kernel Modules
- Efficient Loading: Minimize system overhead.
- Security: Signature verification.
- Compatibility: Match kernel versions.
- Maintainability: Module parameter adjustments.
- Monitoring: Real-time viewing of module status.
2. In-Depth Principles of Linux Kernel Modules
2.1 Working Mechanism of Kernel Modules
Kernel modules are binary files in ELF (Executable and Linkable Format) format, loaded into the kernel address space via the load_module() function. The loading process includes:
- Allocating memory.
- Parsing the ELF header.
- Loading code and data segments.
- Resolving symbols.
- Calling the init function.
Module Dependencies: /lib/modules/$(uname -r)/modules.dep.
Symbol Exporting: EXPORT_SYMBOL() exports functions for use by other modules.
2.2 Module Parameters
Modules support runtime parameters defined via module_param().
Example:
static int my_param = 0;
module_param(my_param, int, S_IRUSR | S_IWUSR);
MODULE_PARM_DESC(my_param, "An integer parameter");
Loading:
sudo modprobe my_module my_param=5
Viewing:
cat /sys/module/my_module/parameters/my_param
2.3 Interaction Between Modules and the Kernel
- printk: Log output.
- kmalloc/kfree: Memory allocation.
- schedule: Scheduling function.
Kernel Space vs User Space: Modules run in kernel space, accessing hardware.
2.4 Security Mechanisms for Modules
-
Signature: CONFIG_MODULE_SIG enables signature checking.
sudo modprobe -v my_module # Check signature -
Forced Signature: CONFIG_MODULE_SIG_FORCE=1.
Generating Signatures: Use the sign-file tool.
2.5 Unloading Mechanism of Modules
When unloading, the exit function is called to release resources. If the module is in use, it cannot be unloaded (use count > 0).
Forced Unloading: rmmod -f (dangerous).
3. Steps to Create Linux Kernel Modules
3.1 Preparing the Development Environment
-
Install kernel headers:
sudo apt install linux-headers-$(uname -r) -
Install build-essential:
sudo apt install build-essential
3.2 Writing Module Code
Create my_module.c:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
static int __init my_module_init(void) {
printk(KERN_INFO "Hello, Kernel Module!\n");
return 0;
}
static void __exit my_module_exit(void) {
printk(KERN_INFO "Goodbye, Kernel Module!\n");
}
module_init(my_module_init);
module_exit(my_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple kernel module");
MODULE_VERSION("0.1");
3.3 Writing a Makefile
obj-m += my_module.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
modules:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean
3.4 Compiling the Module
make
Generates my_module.ko.
3.5 Loading and Testing
sudo insmod my_module.ko
dmesg | tail # View printk
sudo rmmod my_module
dmesg | tail
Using modprobe:
sudo modprobe my_module
sudo modprobe -r my_module
3.6 Example of Module Parameters
Add parameters:
static char *my_string = "default";
module_param(my_string, charp, S_IRUSR | S_IWUSR);
MODULE_PARM_DESC(my_string, "A string parameter");
Loading:
sudo insmod my_module.ko my_string="hello"
cat /sys/module/my_module/parameters/my_string
4. Management Tools for Kernel Modules
4.1 lsmod
Purpose: List loaded modules.
lsmod | grep nfs
4.2 modinfo
Purpose: View module information.
modinfo nfs
Output:
filename: /lib/modules/5.15.0-73-generic/kernel/fs/nfs/nfs.ko.xz
license: GPL
author: Trond Myklebust <[email protected]>
description: NFS client
4.3 insmod/rmmod
Purpose: Load/unload modules.
sudo insmod /path/to/module.ko
sudo rmmod module_name
4.4 modprobe
Purpose: Smart loading, handling dependencies.
sudo modprobe nfs
sudo modprobe -r nfs
4.5 depmod
Purpose: Generate module dependencies.
sudo depmod -a
4.6 /proc/modules
View:
cat /proc/modules
5. Optimization and Advanced Applications of Kernel Modules
5.1 Exporting Module Symbols
Exporting Functions:
int my_function(void) {
return 42;
}
EXPORT_SYMBOL(my_function);
Usage: Other modules can call it via extern int my_function(void);.
5.2 Device Driver Modules
Character Device Example:
#include <linux/fs.h>
#include <linux/cdev.h>
static dev_t dev;
static struct cdev c_dev;
static int my_open(struct inode *inode, struct file *file) {
printk("Device opened\n");
return 0;
}
static struct file_operations fops = {
.open = my_open,
};
static int __init my_device_init(void) {
alloc_chrdev_region(&dev, 0, 1, "my_device");
cdev_init(&c_dev, &fops);
cdev_add(&c_dev, dev, 1);
return 0;
}
static void __exit my_device_exit(void) {
cdev_del(&c_dev);
unregister_chrdev_region(dev, 1);
}
module_init(my_device_init);
module_exit(my_device_exit);
Loading:
sudo insmod my_device.ko
mknod /dev/my_device c <major> 0
cat /dev/my_device
5.3 Module Parameter Arrays
static int my_array[3] = {1, 2, 3};
module_param_array(my_array, int, NULL, S_IRUSR | S_IWUSR);
5.4 Module Version Compatibility
Use MODULE_VERSION(“1.0”).
5.5 Optimizing Module Performance
- Reduce printk: Use ratelimit.
- Asynchronous Loading: Use module_async_probe.
- Memory Pools: Use mempool.
6. Common Issues and Solutions
6.1 Module Loading Failure
Cause: Missing dependencies.
Solution:
sudo modprobe -v my_module
dmesg | tail
6.2 Undefined Symbols
Cause: Not exported.
Solution: Add EXPORT_SYMBOL.
6.3 Version Mismatch
Cause: Incorrect kernel version for the module.
Solution: Recompile.
6.4 Unloading Failure
Cause: use count > 0.
Solution:
lsmod | grep my_module
6.5 Signature Errors
Solution: Disable signature checking or sign the module.
7. Case Studies
7.1 Case 1: Simple Module Development
Code: As in 3.2. Compile and Load: make, insmod. Result: dmesg shows “Hello”.
7.2 Case 2: Character Device Driver
Code: As in 5.2. Usage: mknod, echo > /dev/my_device. Result: Custom device runs.
7.3 Case 3: Network Module Extension
Scenario: Custom netfilter hook. Code: Use NF_HOOK to register. Result: Filters network packets.
8. Future Development of Kernel Modules
With the evolution of the Linux kernel, modules will support eBPF (extended Berkeley Packet Filter), enabling user-space module loading and reducing kernel risks. Rust modules will also become popular, enhancing security.
9. Conclusion
Linux kernel modules are a powerful tool for extending the kernel. By mastering the principles and practical applications, custom functionalities can be built.