Linux内核模块开发完整教程:从Hello World到设备驱动
本文提供完整的Linux内核模块开发指南,帮助开发者从基础的Hello World模块到复杂的设备驱动程序。
1. 内核模块基础概念
Linux内核模块是可以在运行时动态加载和卸载的代码片段。它们扩展了内核的功能而无需重新编译整个内核。
2. 环境准备
首先安装必要的开发工具:
# Ubuntu/Debian
sudo apt-get install build-essential linux-headers-$(uname -r)
# CentOS/RHEL
sudo yum install gcc make kernel-devel kernel-headers
3. Hello World模块
创建最简单的内核模块:
// hello.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple Hello World module");
MODULE_VERSION("0.1");
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);
4. Makefile编写
# Makefile
obj-m += hello.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all:
make -C $(KDIR) M=$(PWD) modules
clean:
make -C $(KDIR) M=$(PWD) clean
5. 编译和加载模块
# 编译模块
make
# 加载模块
sudo insmod hello.ko
# 查看内核日志
dmesg | tail
# 卸载模块
sudo rmmod hello
6. 参数传递
向模块传递参数:
#include <linux/moduleparam.h>
static char *name = "World";
static int times = 1;
module_param(name, charp, S_IRUGO);
module_param(times, int, S_IRUGO);
static int __init hello_init(void) {
int i;
for (i = 0; i < times; i++) {
printk(KERN_INFO "Hello, %s!\n", name);
}
return 0;
}
7. 设备驱动框架
字符设备驱动示例:
#include <linux/fs.h>
#include <linux/uaccess.h>
#define DEVICE_NAME "mydevice"
#define BUFFER_SIZE 1024
static int major_number;
static char buffer[BUFFER_SIZE];
static int buffer_ptr = 0;
static int device_open(struct inode *, struct file *);
static int device_release(struct inode *, struct file *);
static ssize_t device_read(struct file *, char *, size_t, loff_t *);
static ssize_t device_write(struct file *, const char *, size_t, loff_t *);
static struct file_operations fops = {
.read = device_read,
.write = device_write,
.open = device_open,
.release = device_release
};
static int __init init_module(void) {
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 major number %d\n", major_number);
return 0;
}
static void __exit cleanup_module(void) {
unregister_chrdev(major_number, DEVICE_NAME);
printk(KERN_INFO "Unregistered major number %d\n", major_number);
}
8. 内存管理
内核内存分配:
// 分配内存
void *ptr = kmalloc(size, GFP_KERNEL);
// 释放内存
kfree(ptr);
// 页面分配
unsigned long page = __get_free_page(GFP_KERNEL);
// 释放页面
free_page(page);
9. 同步机制
使用自旋锁:
#include <linux/spinlock.h>
static DEFINE_SPINLOCK(my_lock);
// 获取锁
spin_lock(&my_lock);
// 临界区代码
spin_unlock(&my_lock);
10. 调试技巧
- 使用printk进行调试输出
- 检查/proc文件系统
- 使用kgdb进行内核调试
- 启用内核配置选项DEBUG_KERNEL
总结
Linux内核模块开发是一个复杂但强大的技术。通过本文的指导,您可以从简单的Hello World模块开始,逐步掌握设备驱动开发的核心技能。记住始终在虚拟机或测试环境中进行开发,避免损坏生产系统。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END









暂无评论内容