完整教程:Linux 驱动(.ko)基本框架
Linux 驱动(.ko)基本框架
1. 核心组件
- 源代码:
.c文件(驱动实现) - Makefile:指导编译过程
- Kbuild 系统:Linux 内核的构建系统
2. 编译流程
3. Makefile 关键要素
# 指定内核源码路径(必须)
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
# 当前模块目录
PWD := $(shell pwd)
# 模块名称(生成 my_module.ko)
obj-m := my_module.o
# 多文件驱动
my_module-objs := file1.o file2.o
all:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(PWD) clean
驱动代码基本框架(字符设备示例)
1. 最小驱动结构
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#define DEV_NAME "my_dev"
static int major = 0;
static struct cdev my_cdev;
static int my_open(struct inode *inode, struct file *file) {
printk(KERN_INFO "Device opened\n");
return 0;
}
static ssize_t my_read(struct file *file, char __user *buf, size_t count, loff_t *fpos) {
const char *msg = "Hello from kernel!\n";
size_t len = strlen(msg);
if (copy_to_user(buf, msg, min(len, count)))
return -EFAULT;
return min(len, count);
}
static struct file_operations fops = {
.owner = THIS_MODULE,
.open = my_open,
.read = my_read,
};
static int __init my_init(void) {
// 1. 分配设备号
alloc_chrdev_region(&major, 0, 1, DEV_NAME);
// 2. 初始化cdev
cdev_init(&my_cdev, &fops);
my_cdev.owner = THIS_MODULE;
// 3. 添加到系统
cdev_add(&my_cdev, MKDEV(major, 0), 1);
printk(KERN_INFO "Driver loaded, major=%d\n", major);
return 0;
}
static void __exit my_exit(void) {
cdev_del(&my_cdev);
unregister_chrdev_region(MKDEV(major, 0), 1);
printk(KERN_INFO "Driver unloaded\n");
}
module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Simple Character Driver");
2. 关键组件说明
初始化和退出函数
module_init():驱动加载时调用module_exit():驱动卸载时调用
设备操作集
struct file_operations { .owner = THIS_MODULE, .open = my_open, .read = my_read, .write = my_write, .release = my_release, // ... 其他操作 };字符设备注册
alloc_chrdev_region():动态申请设备号cdev_init():初始化cdev结构体cdev_add():添加设备到系统
用户-内核空间交互
copy_to_user():内核→用户空间copy_from_user():用户→内核空间
编译与测试流程
- 编译驱动
make # 生成 my_module.ko - 加载驱动
sudo insmod my_module.ko dmesg | tail # 查看加载日志 - 创建设备节点
sudo mknod /dev/my_dev c 250 0 # 250替换为dmesg中的major号 - 测试驱动
cat /dev/my_dev # 应输出 "Hello from kernel!" - 卸载驱动
sudo rmmod my_module
关键注意事项
内核API兼容性
- 使用
#include <linux/...>而非标准C头文件 - 遵循内核编码规范(如函数/变量命名)
- 使用
并发控制
- 使用互斥锁(
mutex)或自旋锁(spinlock)
- 使用互斥锁(
错误处理
- 所有内核函数需检查返回值
- 确保退出时释放所有资源
调试
printk分级:KERN_DEBUG、KERN_ERR/proc/kallsyms查看符号表strace跟踪系统调用
完整开发建议:参考 Linux 内核文档
Documentation/driver-api/
浙公网安备 33010602011771号