Best viewed on desktop for optimal interactive experience
The Kernel's Plugin System
Imagine if you had to rebuild your entire operating system kernel every time you wanted to add support for a new device or filesystem. That was the reality in early Unix systems. Linux kernel modules changed everything by introducing a plugin system for the kernel - allowing you to extend kernel functionality on the fly without rebooting.
Kernel modules are like LEGO blocks for your operating system. Each module is a piece of code that can be snapped into the running kernel to add new capabilities - device drivers, filesystems, network protocols, or security features. When you plug in a USB device or mount an exotic filesystem, kernel modules spring into action, dynamically extending your kernel's abilities.
This modular architecture makes Linux incredibly flexible. Your distribution can ship with a generic kernel that works on countless hardware configurations, loading only the specific modules needed for your system.
Interactive Kernel Modules Visualization
Explore how kernel modules work, their lifecycle, and development process:
Linux Kernel Modules
What are Kernel Modules?
Kernel modules are pieces of code that can be loaded and unloaded into the kernel on demand. They extend kernel functionality without requiring a reboot.
Common Module Types
Module Management Commands
lsmod
modinfo module_name
insmod module.ko
modprobe module_name
rmmod module_name
depmod -a
Module Locations
Understanding Kernel Modules
What Are Kernel Modules?
// Kernel modules are object files (.ko) that extend kernel functionality // They run in kernel space with full privileges #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> // Module metadata MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("A simple kernel module"); MODULE_VERSION("1.0"); // Module initialization function static int __init hello_init(void) { printk(KERN_INFO "Hello, Kernel!\n"); return 0; // 0 = success, negative = failure } // Module cleanup function static void __exit hello_exit(void) { printk(KERN_INFO "Goodbye, Kernel!\n"); } // Register init and exit functions module_init(hello_init); module_exit(hello_exit);
Module Information
# List loaded modules lsmod Module Size Used by nvidia_drm 61440 3 nvidia_modeset 1232896 14 nvidia_drm nvidia 40873984 789 nvidia_modeset drm_kms_helper 253952 1 nvidia_drm drm 548864 6 drm_kms_helper,nvidia_drm # Detailed module information modinfo ext4 filename: /lib/modules/6.1.0/kernel/fs/ext4/ext4.ko description: Fourth Extended Filesystem author: Theodore Ts'o license: GPL depends: mbcache,jbd2 intree: Y vermagic: 6.1.0 SMP preempt mod_unload # Module parameters modinfo -p i915 modeset:Use kernel modesetting [KMS] (0=disable, 1=on) (int) enable_guc:Enable GuC load for GuC submission (int) enable_fbc:Enable frame buffer compression (int)
Module Management
Loading and Unloading Modules
# Manual module loading insmod /path/to/module.ko insmod module.ko param1=value param2=value # Remove module rmmod module_name # Smart loading with dependency resolution modprobe module_name modprobe -r module_name # Remove with dependencies # Force operations (dangerous!) modprobe -f module_name # Force load rmmod -f module_name # Force remove # Load module with parameters modprobe usbcore autosuspend=2 echo "options usbcore autosuspend=2" > /etc/modprobe.d/usb.conf
Module Dependencies
# View dependencies modprobe --show-depends ext4 insmod /lib/modules/6.1.0/kernel/fs/mbcache.ko insmod /lib/modules/6.1.0/kernel/fs/jbd2/jbd2.ko insmod /lib/modules/6.1.0/kernel/fs/ext4/ext4.ko # Generate dependency database depmod -a # Dependency file cat /lib/modules/$(uname -r)/modules.dep kernel/fs/ext4/ext4.ko: kernel/fs/jbd2/jbd2.ko kernel/fs/mbcache.ko # Module alias cat /lib/modules/$(uname -r)/modules.alias alias fs-ext4 ext4 alias ext3 ext4 alias pci:v00008086d00001234* e1000e
Module Configuration
# /etc/modprobe.d/blacklist.conf # Prevent modules from loading blacklist pcspkr # Disable PC speaker blacklist bluetooth # Disable Bluetooth # /etc/modprobe.d/options.conf # Set module parameters options i915 enable_fbc=1 options snd_hda_intel power_save=1 # /etc/modules-load.d/modules.conf # Load modules at boot # One module per line vboxdrv vboxnetflt nvidia # /etc/modprobe.d/aliases.conf # Create module aliases alias netdev-eth0 e1000e alias sound-slot-0 snd_hda_intel
Developing Kernel Modules
Basic Module Structure
// mydriver.c - A complete kernel module example #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/device.h> #include <linux/cdev.h> #include <linux/uaccess.h> #define DEVICE_NAME "mydevice" #define CLASS_NAME "myclass" MODULE_LICENSE("GPL"); MODULE_AUTHOR("Developer Name"); MODULE_DESCRIPTION("A simple character device driver"); static int major_number; static struct class *dev_class = NULL; static struct device *dev_device = NULL; static int device_open_count = 0; // Device operations static int dev_open(struct inode *inode, struct file *file) { device_open_count++; printk(KERN_INFO "Device opened %d time(s)\n", device_open_count); return 0; } static int dev_release(struct inode *inode, struct file *file) { printk(KERN_INFO "Device closed\n"); return 0; } static ssize_t dev_read(struct file *file, char __user *buffer, size_t len, loff_t *offset) { char message[] = "Hello from kernel module!\n"; size_t message_len = strlen(message); if (*offset >= message_len) return 0; if (len > message_len - *offset) len = message_len - *offset; if (copy_to_user(buffer, message + *offset, len)) return -EFAULT; *offset += len; return len; } static ssize_t dev_write(struct file *file, const char __user *buffer, size_t len, loff_t *offset) { printk(KERN_INFO "Received %zu bytes from user\n", len); return len; } // File operations structure static struct file_operations fops = { .owner = THIS_MODULE, .open = dev_open, .read = dev_read, .write = dev_write, .release = dev_release, }; // Module initialization static int __init mydriver_init(void) { printk(KERN_INFO "Initializing %s\n", DEVICE_NAME); // Register character device major_number = register_chrdev(0, DEVICE_NAME, &fops); if (major_number < 0) { printk(KERN_ALERT "Failed to register major number\n"); return major_number; } printk(KERN_INFO "Registered with major number %d\n", major_number); // Create device class dev_class = class_create(THIS_MODULE, CLASS_NAME); if (IS_ERR(dev_class)) { unregister_chrdev(major_number, DEVICE_NAME); printk(KERN_ALERT "Failed to create device class\n"); return PTR_ERR(dev_class); } // Create device dev_device = device_create(dev_class, NULL, MKDEV(major_number, 0), NULL, DEVICE_NAME); if (IS_ERR(dev_device)) { class_destroy(dev_class); unregister_chrdev(major_number, DEVICE_NAME); printk(KERN_ALERT "Failed to create device\n"); return PTR_ERR(dev_device); } printk(KERN_INFO "Device created successfully\n"); return 0; } // Module cleanup static void __exit mydriver_exit(void) { device_destroy(dev_class, MKDEV(major_number, 0)); class_unregister(dev_class); class_destroy(dev_class); unregister_chrdev(major_number, DEVICE_NAME); printk(KERN_INFO "Device unregistered\n"); } module_init(mydriver_init); module_exit(mydriver_exit);
Building Kernel Modules
# Makefile for kernel module obj-m += mydriver.o # For modules with multiple source files # mymodule-objs := file1.o file2.o file3.o # obj-m += mymodule.o KERNEL_DIR ?= /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) all: $(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules clean: $(MAKE) -C $(KERNEL_DIR) M=$(PWD) clean install: $(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules_install depmod -a # Build commands # make - Build module # make clean - Clean build files # make install - Install module
Module Parameters
// Defining module parameters #include <linux/moduleparam.h> static int debug_level = 1; static char *device_name = "default"; static int buffer_size = 4096; // Parameter declarations module_param(debug_level, int, 0644); MODULE_PARM_DESC(debug_level, "Debug level (0-3)"); module_param(device_name, charp, 0444); MODULE_PARM_DESC(device_name, "Device name string"); module_param(buffer_size, int, 0644); MODULE_PARM_DESC(buffer_size, "Internal buffer size"); // Array parameters static int irq_list[4] = {-1, -1, -1, -1}; static int irq_count; module_param_array(irq_list, int, &irq_count, 0444); MODULE_PARM_DESC(irq_list, "List of IRQ numbers"); // Usage // insmod mymodule.ko debug_level=3 device_name="custom" // echo 2 > /sys/module/mymodule/parameters/debug_level
Kernel Module Debugging
Kernel Logging
// Kernel log levels #define KERN_EMERG "<0>" // System is unusable #define KERN_ALERT "<1>" // Action must be taken immediately #define KERN_CRIT "<2>" // Critical conditions #define KERN_ERR "<3>" // Error conditions #define KERN_WARNING "<4>" // Warning conditions #define KERN_NOTICE "<5>" // Normal but significant #define KERN_INFO "<6>" // Informational #define KERN_DEBUG "<7>" // Debug-level messages // Using printk printk(KERN_INFO "Module loaded\n"); printk(KERN_ERR "Error: Invalid parameter\n"); // Modern alternatives pr_info("Informational message\n"); pr_err("Error message\n"); pr_debug("Debug message\n"); // Compiled out unless DEBUG defined // Device-specific logging dev_info(dev, "Device initialized\n"); dev_err(dev, "Device error occurred\n"); // Dynamic debug pr_debug("Debug info: value=%d\n", value); // Enable: echo 'module mymodule +p' > /sys/kernel/debug/dynamic_debug/control
Debugging Techniques
# View kernel logs dmesg | tail -50 journalctl -k -f # Follow kernel messages # Enable debug messages echo 8 > /proc/sys/kernel/printk # All messages echo 'module mymodule +p' > /sys/kernel/debug/dynamic_debug/control # Kernel debugging with GDB # Requires kernel compiled with CONFIG_DEBUG_INFO=y gdb vmlinux /proc/kcore # System tap for dynamic instrumentation stap -e 'probe module("mymodule").function("*") { println(probefunc()) }' # Ftrace for function tracing echo function > /sys/kernel/debug/tracing/current_tracer echo 'mymodule:*' > /sys/kernel/debug/tracing/set_ftrace_filter echo 1 > /sys/kernel/debug/tracing/tracing_on cat /sys/kernel/debug/tracing/trace
Common Module Types
Device Drivers
// Character device driver static struct cdev my_cdev; static dev_t dev_num; // Block device driver static struct block_device_operations my_blkdev_ops = { .owner = THIS_MODULE, .open = my_open, .release = my_release, }; // Network device driver static struct net_device_ops my_netdev_ops = { .ndo_open = my_net_open, .ndo_stop = my_net_stop, .ndo_start_xmit = my_net_xmit, };
Filesystem Modules
// Filesystem registration #include <linux/fs.h> static struct file_system_type my_fs_type = { .owner = THIS_MODULE, .name = "myfs", .mount = my_mount, .kill_sb = kill_block_super, .fs_flags = FS_REQUIRES_DEV, }; static int __init myfs_init(void) { return register_filesystem(&my_fs_type); } static void __exit myfs_exit(void) { unregister_filesystem(&my_fs_type); }
Network Protocol Modules
// Protocol handler #include <linux/netdevice.h> static struct packet_type my_packet_type = { .type = htons(ETH_P_CUSTOM), .func = my_packet_rcv, }; static int __init my_proto_init(void) { dev_add_pack(&my_packet_type); return 0; }
Security Considerations
Module Signing
# Kernel configuration for module signing CONFIG_MODULE_SIG=y CONFIG_MODULE_SIG_FORCE=y CONFIG_MODULE_SIG_SHA256=y # Sign a module scripts/sign-file sha256 \ certs/signing_key.pem \ certs/signing_key.x509 \ drivers/mymodule.ko # Verify module signature modinfo mymodule.ko | grep signature signature: 3A:7C:3F:... # Check if module signing is enforced cat /sys/module/module/parameters/sig_enforce
Capabilities and Permissions
// Check capabilities in module #include <linux/capability.h> if (!capable(CAP_SYS_ADMIN)) { printk(KERN_ERR "Permission denied\n"); return -EPERM; } // Access control static int my_open(struct inode *inode, struct file *file) { // Check if user has permission if (!uid_eq(current_uid(), GLOBAL_ROOT_UID)) return -EACCES; return 0; }
Performance Optimization
Memory Management
// Kernel memory allocation #include <linux/slab.h> // Allocate memory void *ptr = kmalloc(size, GFP_KERNEL); // May sleep void *ptr = kmalloc(size, GFP_ATOMIC); // Cannot sleep void *ptr = kzalloc(size, GFP_KERNEL); // Zeroed memory void *ptr = vmalloc(size); // Large allocations // Free memory kfree(ptr); vfree(ptr); // Memory pools static struct kmem_cache *my_cache; my_cache = kmem_cache_create("my_cache", sizeof(struct my_struct), 0, SLAB_HWCACHE_ALIGN, NULL); struct my_struct *obj = kmem_cache_alloc(my_cache, GFP_KERNEL); kmem_cache_free(my_cache, obj); kmem_cache_destroy(my_cache);
Locking and Synchronization
// Spinlocks (for short critical sections) #include <linux/spinlock.h> static DEFINE_SPINLOCK(my_lock); spin_lock(&my_lock); // Critical section spin_unlock(&my_lock); // Mutexes (can sleep) #include <linux/mutex.h> static DEFINE_MUTEX(my_mutex); mutex_lock(&my_mutex); // Critical section mutex_unlock(&my_mutex); // Read-Write locks static DEFINE_RWLOCK(my_rwlock); read_lock(&my_rwlock); // Read critical section read_unlock(&my_rwlock); write_lock(&my_rwlock); // Write critical section write_unlock(&my_rwlock); // RCU (Read-Copy-Update) #include <linux/rcupdate.h> rcu_read_lock(); // Read-side critical section rcu_read_unlock();
Module Troubleshooting
Common Issues
# Module version mismatch modprobe: ERROR: could not insert 'module': Exec format error # Solution: Rebuild module against current kernel # Symbol errors modprobe: ERROR: could not insert 'module': Unknown symbol # Check: dmesg | tail # Solution: Load dependencies or export symbols # Module in use rmmod: ERROR: Module module is in use # Check: lsmod | grep module # Solution: Stop using processes or force remove # Tainted kernel # Check taint status cat /proc/sys/kernel/tainted # Flags: # P - Proprietary module # F - Module forced # S - SMP unsafe # R - Forced rmmod # M - Machine check exception # B - Bad page # U - Userspace-defined # D - Died recently # A - ACPI overridden # W - Warning issued # C - Staging driver # I - Firmware workaround # O - Out-of-tree module # E - Unsigned module # L - Soft lockup # K - Live patched
Best Practices
- Always check return values - Handle errors gracefully
- Use proper locking - Prevent race conditions
- Minimize time in kernel space - Return to userspace quickly
- Validate user input - Never trust data from userspace
- Clean up resources - Implement proper exit handlers
- Use kernel coding style - Follow Documentation/CodingStyle
- Test thoroughly - Use various kernel configurations
- Document module parameters - Help users understand options
- Consider security - Implement proper access controls
- Profile performance - Monitor CPU and memory usage
Conclusion
Kernel modules are the secret to Linux's incredible flexibility and hardware support. They allow you to extend kernel functionality without recompilation, add device support on the fly, and develop kernel code with relative ease. From simple "Hello World" modules to complex device drivers managing cutting-edge hardware, modules are essential to the Linux ecosystem.
Our interactive visualization showed how modules load into kernel space, resolve dependencies, and interact with the core kernel. Whether you're developing device drivers, debugging hardware issues, or just curious about how Linux manages its modular architecture, understanding kernel modules opens up a powerful dimension of system programming.
Remember: with great power comes great responsibility. Kernel modules run with the highest privileges, so a single bug can crash your entire system. Always develop and test carefully!
← Back to Package Management ← Back to Linux Concepts