Introduction
Kernel modules are pieces of code that can be dynamically loaded into the Linux kernel to extend its functionality without the need for rebooting. This lesson covers the complete lifecycle of kernel module development, including development, testing, submission, code review, and release processes.
Prerequisites
- Proficiency in C programming
- Understanding of Linux system architecture
- Familiarity with Linux command line and basic shell scripting
1. Setting Up the Development Environment
1.1 Install Required Packages
Ensure that your system has the necessary packages for kernel development. On Debian-based systems, you can install these with:
sudo apt-get update
sudo apt-get install build-essential linux-headers-$(uname -r)
1.2 Kernel Source Code
Download the kernel source code from kernel.org.
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.4.tar.xz
tar -xvf linux-5.4.tar.xz
cd linux-5.4
2. Writing a Kernel Module
2.1 Basic Structure of a Kernel Module
A kernel module requires two essential functions: init
and exit
.
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
static int __init hello_init(void) {
printk(KERN_INFO "Hello, World!\n");
return 0;
}
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");
Save this code in a file named hello.c
.
2.2 Compiling the Module
Create a Makefile
for the 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) clean
Run make
to compile the module:
make
2.3 Loading and Unloading the Module
To load the module:
sudo insmod hello.ko
To check the module is loaded:
lsmod | grep hello
To unload the module:
sudo rmmod hello
Check system logs for module messages:
dmesg | tail
3. Advanced Kernel Module Features
3.1 Character Device Driver Example
Extend the basic module to create a simple character device driver.
3.1.1 Device Initialization
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#define DEVICE_NAME "simple_char_dev"
#define BUF_LEN 80
static int major;
static char msg[BUF_LEN];
static struct cdev c_dev;
static struct class *cl;
static int device_open(struct inode *inode, struct file *file) {
static int counter = 0;
sprintf(msg, "Device opened %d times\n", counter++);
return 0;
}
static ssize_t device_read(struct file *filp, char __user *buffer, size_t length, loff_t * offset) {
copy_to_user(buffer, msg, strlen(msg));
return strlen(msg);
}
static struct file_operations fops = {
.owner = THIS_MODULE,
.open = device_open,
.read = device_read,
};
static int __init simple_char_init(void) {
major = register_chrdev(0, DEVICE_NAME, &fops);
if (major < 0) {
pr_alert("Registering char device failed with %d\n", major);
return major;
}
if (IS_ERR(cl = class_create(THIS_MODULE, "char"))) {
unregister_chrdev(major, DEVICE_NAME);
return PTR_ERR(cl);
}
if (IS_ERR(device_create(cl, NULL, MKDEV(major, 0), NULL, DEVICE_NAME))) {
class_destroy(cl);
unregister_chrdev(major, DEVICE_NAME);
return PTR_ERR(cl);
}
cdev_init(&c_dev, &fops);
if (cdev_add(&c_dev, MKDEV(major, 0), 1) == -1) {
device_destroy(cl, MKDEV(major, 0));
class_destroy(cl);
unregister_chrdev(major, DEVICE_NAME);
return -1;
}
pr_info("Simple char device registered with major number %d\n", major);
return 0;
}
static void __exit simple_char_exit(void) {
cdev_del(&c_dev);
device_destroy(cl, MKDEV(major, 0));
class_destroy(cl);
unregister_chrdev(major, DEVICE_NAME);
pr_info("Simple char device unregistered\n");
}
module_init(simple_char_init);
module_exit(simple_char_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A Simple Character Device Driver");
3.1.2 Compiling and Testing
Use the same Makefile
structure as before. Compile and load the module:
make
sudo insmod simple_char_dev.ko
Test the device:
cat /dev/simple_char_dev
Unload the module:
sudo rmmod simple_char_dev
4. Testing Kernel Modules
4.1 Static Analysis
Use tools like checkpatch.pl
to ensure your code follows kernel coding standards:
./scripts/checkpatch.pl --file path/to/your/module.c
4.2 Dynamic Testing
- Unit Tests: Write tests for individual components.
- Integration Tests: Ensure the module works correctly with other kernel components.
5. Submission Process
5.1 Preparing for Submission
- Clean your code and remove debug logs.
- Ensure it follows the Linux kernel coding style.
5.2 Creating Patches
Generate a patch file using git
:
git format-patch -1
5.3 Submitting Patches
Send patches to the appropriate mailing list. Find the relevant mailing list using:
./scripts/get_maintainer.pl -f path/to/your/module.c
Use git send-email
to send patches:
git send-email --to [email protected] 0001-Your-Patch.patch
6. Code Review Process
Respond to feedback from maintainers and the community. Revise your code and resubmit patches as needed.
7. Release Process
Once the patch is accepted, it will be merged into the mainline kernel. Monitor the kernel mailing list for updates and address any issues that arise from the community or during testing.
Conclusion
Kernel module development is a complex but rewarding aspect of Linux programming. This lesson has covered the entire process from development to release, providing you with the foundational knowledge and practical examples to get started.