项目简介
在本章节中,我们将创建一个名为“生命信号”的Linux内核驱动。该驱动会每秒钟自动递增一个计数器,并通过 /proc
文件系统或 sysfs
向用户空间提供读取接口。这个项目是理解内核定时器、文件系统接口和内核模块编程的绝佳起点。
驱动功能
- 使用内核定时器实现每秒自动递增的计数器
- 通过
/proc
或sysfs
暴露计数器值 - 支持多用户同时读取
- 在QEMU环境中完美运行
软件架构
关键数据结构
结构体 | 描述 | 用途 |
---|---|---|
timer_list |
内核定时器结构 | 用于实现每秒递增的定时器 |
proc_dir_entry |
/proc 文件入口 | 创建 /proc/counter 文件 |
file_operations |
文件操作结构 | 定义文件的读写操作 |
代码实现
1. 驱动初始化
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#define DRIVER_NAME "heartbeat"
#define PROC_NAME "counter"
static unsigned long counter = 0;
static struct timer_list my_timer;
static struct proc_dir_entry *proc_entry;
static void timer_callback(struct timer_list *t)
{
counter++;
mod_timer(&my_timer, jiffies + HZ);
}
2. /proc 文件操作
static ssize_t proc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
char str[20];
int len;
if (*ppos > 0)
return 0;
len = snprintf(str, sizeof(str), "%lu\n", counter);
if (copy_to_user(buf, str, len))
return -EFAULT;
*ppos = len;
return len;
}
static const struct proc_ops proc_fops = {
.proc_read = proc_read,
};
3. 模块初始化和退出
static int __init heartbeat_init(void)
{
// 初始化定时器
timer_setup(&my_timer, timer_callback, 0);
mod_timer(&my_timer, jiffies + HZ);
// 创建 /proc 文件
proc_entry = proc_create(PROC_NAME, 0444, NULL, &proc_fops);
if (!proc_entry) {
printk(KERN_ERR "Failed to create /proc/%s\n", PROC_NAME);
return -ENOMEM;
}
printk(KERN_INFO "Heartbeat driver loaded\n");
return 0;
}
static void __exit heartbeat_exit(void)
{
del_timer(&my_timer);
remove_proc_entry(PROC_NAME, NULL);
printk(KERN_INFO "Heartbeat driver unloaded\n");
}
module_init(heartbeat_init);
module_exit(heartbeat_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple heartbeat driver");
编译和测试
- 编写 Makefile 文件
- 使用
make
命令编译驱动 - 使用
insmod heartbeat.ko
加载驱动 - 使用
cat /proc/counter
查看计数器值 - 观察计数器每秒自动递增
总结
通过本项目,我们实现了一个简单的“生命信号”驱动,掌握了以下核心技能:
- Linux内核模块的编写和编译
- 内核定时器的使用
- /proc 文件系统的接口实现
- 用户空间与内核空间的数据交换
这个驱动虽然简单,但包含了Linux驱动开发的基本要素,为后续更复杂的项目打下了坚实基础。