字符设备驱动LKM中read()与write()函数实现问题求助
问题描述
尝试创建一个简单的字符设备LKM,但read()和write()函数实现存在问题:执行echo hi > /dev/simple_character_device能确认写入正确字节数,但执行cat读取设备内容时,会持续循环输出'hi'直到出现错误地址。尝试用全局计数器跟踪写入字节数但未解决问题,需要正确实现read和write函数。
原代码:
#include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/uaccess.h> #include <linux/slab.h> #include <linux/string.h> MODULE_LICENSE("GPL"); #define BUFFER 1024 char * buffer_data; // Count open and closed size_t current_count; int my_open(struct inode *, struct file *); int my_release(struct inode *, struct file *); ssize_t my_read(struct file *, char __user *, size_t count, loff_t *); ssize_t my_write(struct file *, const char __user *, size_t count, loff_t *offp); static struct file_operations fops = { .owner = THIS_MODULE, .open = my_open, .release = my_release, .read = my_read, .write = my_write }; int reg_init(void) { // Allocate memory to store information buffer_data = kmalloc(BUFFER, GFP_KERNEL); // Use Kernel Flag register_chrdev(240, "simple_character_device", &fops); printk(KERN_ALERT "Init Allocating Memory"); return 0; } void reg_exit(void) { // Free and unregister device and data kfree(buffer_data); unregister_chrdev(240, "simple_character_device"); printk(KERN_ALERT "Deregister Simple Character Device"); } int my_open(struct inode *inode, struct file *file){ printk(KERN_ALERT "Open File Device.\n"); return 0; } int my_release(struct inode *inode, struct file *file){ printk(KERN_ALERT "Close File Device.\n"); return 0; } ssize_t my_read(struct file *filp, char __user *buff, size_t count, loff_t *offp){ // Check if we are reading within the Buffer Size if(BUFFER - *offp < 0){ printk(KERN_ALERT "Out of buffer range.\n"); return -EFAULT; } // Check if we fail to copy to user if (copy_to_user(buff, buffer_data, current_count) != 0){ printk(KERN_ALERT "Failed to send character to user\n"); return -EFAULT; } (*offp) += current_count; printk(KERN_ALERT "Read %zu bytes.\n", current_count); return current_count; } ssize_t my_write(struct file *filp, const char __user *buff, size_t count, loff_t *offp){ // We need to get data FROM the user space // Make sure we are reading within the buffer if (*offp >= BUFFER || BUFFER - count < *offp){ printk(KERN_ALERT "ATTEMPTING TO WRITE TO OUSIDE OF BUFFER!\n"); return EFAULT; } // Get the amount of bytes from the user copy_from_user(buffer_data + *offp, buff, count); *offp += count; printk(KERN_ALERT "Wrote %zu to the device.\n", count); current_count = count; return current_count; } module_init(reg_init); module_exit(reg_exit);
问题分析
- 全局计数器设计错误:
current_count仅记录单次写入的字节数,未跟踪设备中实际存储的总字节数;且read函数始终从buffer起始位置读取,未利用offp偏移量定位当前读取位置,导致每次读取都返回相同内容。 - 未正确返回EOF:当
offp偏移量超过实际写入的总字节数时,read函数未返回0(表示文件结束),用户空间工具如cat会持续发起读取请求,直到触发错误。 - write函数逻辑缺陷:边界检查逻辑错误,未计算实际可写入的字节数;未处理
copy_from_user的错误返回值;每次写入覆盖current_count,丢失总长度信息。 - 未初始化全局变量:
buffer_data分配后未清零,current_count未初始化,可能导致读取到垃圾数据。
修复后的代码
#include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/uaccess.h> #include <linux/slab.h> #include <linux/string.h> MODULE_LICENSE("GPL"); #define BUFFER_SIZE 1024 char *buffer_data; size_t total_written; // 记录设备中存储的总字节数 int my_open(struct inode *, struct file *); int my_release(struct inode *, struct file *); ssize_t my_read(struct file *, char __user *, size_t count, loff_t *); ssize_t my_write(struct file *, const char __user *, size_t count, loff_t *offp); static struct file_operations fops = { .owner = THIS_MODULE, .open = my_open, .release = my_release, .read = my_read, .write = my_write }; int reg_init(void) { // 分配内存并清零,避免垃圾数据 buffer_data = kzalloc(BUFFER_SIZE, GFP_KERNEL); if (!buffer_data) { printk(KERN_ALERT "Failed to allocate buffer memory"); return -ENOMEM; } // 注册字符设备,检查返回值 int ret = register_chrdev(240, "simple_character_device", &fops); if (ret < 0) { printk(KERN_ALERT "Failed to register character device"); kfree(buffer_data); return ret; } total_written = 0; printk(KERN_ALERT "Simple character device initialized"); return 0; } void reg_exit(void) { kfree(buffer_data); unregister_chrdev(240, "simple_character_device"); printk(KERN_ALERT "Simple character device deregistered"); } int my_open(struct inode *inode, struct file *file){ printk(KERN_INFO "Device opened"); return 0; } int my_release(struct inode *inode, struct file *file){ printk(KERN_INFO "Device closed"); return 0; } ssize_t my_read(struct file *filp, char __user *buff, size_t count, loff_t *offp){ // 检查偏移是否超出总写入长度,返回0表示EOF if (*offp >= total_written) { return 0; } // 计算实际可读的字节数:取用户请求数和剩余字节数的较小值 size_t read_len = min(count, total_written - *offp); // 从当前偏移位置复制数据到用户空间 if (copy_to_user(buff, buffer_data + *offp, read_len) != 0) { printk(KERN_ALERT "Failed to copy data to user"); return -EFAULT; } // 更新偏移量 *offp += read_len; printk(KERN_INFO "Read %zu bytes, offset now %lld", read_len, *offp); return read_len; } ssize_t my_write(struct file *filp, const char __user *buff, size_t count, loff_t *offp){ // 计算实际可写入的字节数:取用户请求数和剩余缓冲区空间的较小值 size_t write_len = min(count, BUFFER_SIZE - *offp); if (write_len == 0) { printk(KERN_ALERT "Buffer is full, cannot write more data"); return -ENOSPC; } // 从用户空间复制数据到内核缓冲区 if (copy_from_user(buffer_data + *offp, buff, write_len) != 0) { printk(KERN_ALERT "Failed to copy data from user"); return -EFAULT; } // 更新偏移量和总写入字节数 *offp += write_len; if (*offp > total_written) { total_written = *offp; } printk(KERN_INFO "Wrote %zu bytes, total stored %zu", write_len, total_written); return write_len; } module_init(reg_init); module_exit(reg_exit);
关键修改说明
- 用
total_written全局变量记录设备中存储的总字节数,替代原有的current_count,避免单次写入覆盖长度信息。 read函数:- 当偏移量超过总写入长度时返回0,触发用户空间的EOF逻辑,终止循环读取。
- 根据偏移量计算剩余可读字节,从正确的位置读取数据,而非始终从buffer起始位置读取。
- 实际读取长度取用户请求数和剩余字节数的较小值,避免越界。
write函数:- 计算实际可写入的字节数,避免缓冲区溢出。
- 处理
copy_from_user的错误返回值,返回正确的错误码。 - 更新
total_written以反映设备中存储的总数据量。
- 初始化阶段用
kzalloc分配内存并清零,避免垃圾数据;添加设备注册的错误检查,确保资源清理。
内容的提问来源于stack exchange,提问作者Nathan
相关产品推荐
相关产品推荐

