我试图写一个简单的字符设备/ LKM读取,写入和寻求。 我一直有这个问题很多,但一直在努力/解决了几个星期,一直无法让它正常工作。 目前,我的模块正确安装和卸载正确,但如果我尝试回显设备驱动程序文件的terminal崩溃,当我尝试从它读取使用猫它返回杀害。
这个模块的步骤:
首先,我运行make -C / lib / modules / $(uname -r)/ build M = $ PWD模块
对于我的内核,uname -r是4.10.17新内核
我使用sudo insmod simple_char_driver.ko安装模块
如果我运行lsmod,则列出模块
如果我运行dmesg,我的init函数“This device is now open”中的KERN_ALERT会正确触发。
另外,如果我运行sudo rmmod,那个函数“这个设备现在closures”KERN_ALERT也会正确触发。
该模块也正确显示在猫/ proc /设备
我使用sudo mknod -m 777 / dev / simple_char_driver c 240 0在/ dev中创build了设备驱动程序文件
在做这个文件之前,我确定了240主号码还没有被使用。
我的设备驱动程序c文件有以下代码:
#include<linux/init.h> #include<linux/module.h> #include<linux/fs.h> #include<linux/slab.h> #include<asm/uaccess.h> #define BUFFER_SIZE 1024 MODULE_LICENSE("GPL"); //minor nunmber 0; static int place_in_buffer = 0; static int end_of_buffer = 1024; static int MAJOR_NUMBER = 240; char* DEVICE_NAME = "simple_char_driver"; typedef struct{ char* buf; }buffer; char *device_buffer; static int closeCounter=0; static int openCounter=0; ssize_t simple_char_driver_read (struct file *pfile, char __user *buffer, size_t length, loff_t *offset){ int bytesRead = 0; if (*offset >=BUFFER_SIZE){ bytesRead = 0; } if (*offset + length > BUFFER_SIZE){ length = BUFFER_SIZE - *offset; } printk(KERN_INFO "Reading from device\n"); if (copy_to_user(buffer, device_buffer + *offset, length) != 0){ return -EFAULT; } copy_to_user(buffer, device_buffer + *offset, length); *offset += length; printk(KERN_ALERT "Read: %s", buffer); printk(KERN_ALERT "%d bytes read\n", bytesRead); return 0; } ssize_t simple_char_driver_write (struct file *pfile, const char __user *buffer, size_t length, loff_t *offset){ int nb_bytes_to_copy; if (BUFFER_SIZE - 1 -*offset <= length) { nb_bytes_to_copy= BUFFER_SIZE - 1 -*offset; printk("BUFFER_SIZE - 1 -*offset <= length"); } else if (BUFFER_SIZE - 1 - *offset > length) { nb_bytes_to_copy = length; printk("BUFFER_SIZE - 1 -*offset > length"); } printk(KERN_INFO "Writing to device\n"); if (*offset + length > BUFFER_SIZE) { printk("sorry, can't do that. "); return -1; } printk("about to copy from device"); copy_from_user(device_buffer + *offset, buffer, nb_bytes_to_copy); device_buffer[*offset + nb_bytes_to_copy] = '\0'; *offset += nb_bytes_to_copy; return nb_bytes_to_copy; } int simple_char_driver_open (struct inode *pinode, struct file *pfile) { printk(KERN_ALERT"This device is now open"); openCounter++; printk(KERN_ALERT "This device has been opened this many times: %d\n", openCounter); return 0; } int simple_char_driver_close (struct inode *pinode, struct file *pfile) { printk(KERN_ALERT"This device is now closed"); closeCounter++; printk(KERN_ALERT "This device has been closed this many times: %d\n", closeCounter); return 0; } loff_t simple_char_driver_seek (struct file *pfile, loff_t offset, int whence) { printk(KERN_ALERT"We are now seeking!"); switch(whence){ case 0:{ if(offset<= end_of_buffer && offset >0){ place_in_buffer = offset; printk(KERN_ALERT" this is where we are in the buffer: %d\n", place_in_buffer); } else{ printk(KERN_ALERT"ERROR you are attempting to go ouside the Buffer"); } break;//THIS IS SEEK_SET } case 1:{ if(((place_in_buffer+offset)<= end_of_buffer)&&((place_in_buffer+offset)>0)){ place_in_buffer = place_in_buffer+offset; printk(KERN_ALERT" this is where we are in the buffer: %d\n", place_in_buffer); } else{ printk(KERN_ALERT"ERROR you are attempting to go ouside the Buffer"); } break; } case 2:{//THIS IS SEEK END if((end_of_buffer-offset)>=0&& offset>0){ place_in_buffer = end_of_buffer-offset; printk(KERN_ALERT" this is where we are in the buffer: %d\n", place_in_buffer); } else{ printk(KERN_ALERT"ERROR you are attempting to go ouside the Buffer"); } break; } default:{ } } printk(KERN_ALERT"I sought %d\n", whence); return place_in_buffer; } struct file_operations simple_char_driver_file_operations = { .owner = THIS_MODULE, .read = simple_char_driver_read, .write = simple_char_driver_write, .open = simple_char_driver_open, .llseek = &simple_char_driver_seek, .release = simple_char_driver_close, }; static int simple_char_driver_init(void) { printk(KERN_ALERT "inside %s function\n",__FUNCTION__); register_chrdev(MAJOR_NUMBER,DEVICE_NAME, &simple_char_driver_file_operations); device_buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL); return 0; } static void simple_char_driver_exit(void) { printk(KERN_ALERT "inside %s function\n",__FUNCTION__); unregister_chrdev(MAJOR_NUMBER, DEVICE_NAME); kfree(device_buffer); } module_init(simple_char_driver_init); module_exit(simple_char_driver_exit);
正如我之前所说,这个文件正确没有错误或警告。 但是,目前如果我尝试回显到设备文件
使用:echo“hello world”>> / dev / simple_char_driver
我正在使用的terminal崩溃
如果我然后重新打开terminal,并使用:cat / dev / simple_char_driver
然后terminal返回杀害。
我完全失去了什么是错误的,我一直在寻找一个很长一段时间没有成功的解决scheme。 如果有人有任何问题,请告诉我。
编辑:作为下面的用户build议,我从我的读取和写入方法,除了printk和返回,以确保function被触发的所有代码。 当我使用回声时,dmesg显示写入printk被触发,并且设备(我已经打开)closures。 当我试图捕捉设备文件时,dmesg显示设备重新打开,“从设备准备好”printk显示成功,然后设备再次closures。 然而,回声并没有真正find任何东西从设备文件中读取,尽pipe我之前已经回应了“Hello world”。
编辑
最终的function读写function如下:
ssize_t simple_char_driver_read (struct file *pfile, char __user *buffer, size_t length, loff_t *offset) { if (*offset > BUFFER_SIZE) { printk("offset is greater than buffer size"); return 0; } if (*offset + length > BUFFER_SIZE) { length = BUFFER_SIZE - *offset; } if (copy_to_user(buffer, device_buffer + *offset, length) != 0) { return -EFAULT; } *offset += length; return length; } ssize_t simple_char_driver_write (struct file *pfile, const char __user *buffer, size_t length, loff_t *offset){ /* *buffer is the userspace buffer where you are writing the data you want to be written in the device file*/ /* length is the length of the userspace buffer*/ /* current position of the opened file*/ /* copy_from_user function: destination is device_buffer and source is the userspace buffer *buffer */ int nb_bytes_to_copy; if (BUFFER_SIZE - 1 -*offset <= length) { nb_bytes_to_copy= BUFFER_SIZE - 1 -*offset; printk("BUFFER_SIZE - 1 -*offset <= length"); } else if (BUFFER_SIZE - 1 - *offset > length) { nb_bytes_to_copy = length; printk("BUFFER_SIZE - 1 -*offset > length"); } printk(KERN_INFO "Writing to device\n"); if (*offset + length > BUFFER_SIZE) { printk("sorry, can't do that. "); return -1; } printk("about to copy from device"); copy_from_user(device_buffer + *offset, buffer, nb_bytes_to_copy); device_buffer[*offset + nb_bytes_to_copy] = '\0'; *offset += nb_bytes_to_copy; return nb_bytes_to_copy; }
你的代码总的来说还有很多不足之处,但是我现在看到的是你的.write
实现可能是可疑的。 有两个可能的错误 – 没有缓冲区边界检查和无视空终止,这可能导致strlen()
未定义行为。
首先,你知道缓冲区的大小 – BUFFER_SIZE
。 因此,您应该执行*offset + length < BUFFER_SIZE
。 它应该是<
而不是<=
因为无论如何最后一个字节将被保留为空终止。 所以,如果没有可用空间( else
分支或>=
),这样的检查应该立即返回方法。 我不能肯定地说你是否应该返回0
来报告没有写入任何内容,或者使用负值来返回一个错误代码,比如说-ENOBUFS
或者-ENOSPC
。 无论如何,该方法的返回值是ssize_t
意味着可以返回负值。
其次,如果您的第一次检查成功,您的方法应计算可用于写作的实际空间。 也就是说,你可以利用MIN(A, B)
宏来做到这一点。 换句话说,你最好创建一个变量,比如说nb_bytes_to_copy
并初始化它,就像nb_bytes_to_copy = MIN(BUFFER_SIZE - 1 - *offset, length)
以便稍后在copy_from_user()
调用中使用它。 如果用户请求写入从1021
字节偏移量开始的5
个字节的数据,那么驱动程序将允许只写入2
个字节的数据 – 比如说he
而不是hello
。 此外,返回值应设置为nb_bytes_to_copy
以便调用者能够检测到缓冲区空间不足。
最后,不要忘记空终止。 一旦你完成了
copy_from_user(device_buffer + *offset, buffer, nb_bytes_to_copy);
你应该注意做一些类似的事情
device_buffer[*offset + nb_bytes_copy] = '\0';
另外,如果我记得正确的话,你可以使用像strncopy_from_user()
这样的特殊函数来确保数据是用隐式空终止符来复制的。
另外,虽然null终止的写入不会导致随后strlen()
,但我怀疑你是否需要它。 你可以简单地做*offset += nb_bytes_to_copy
。
顺便说一下,我建议以更具描述性的方式命名参数/变量。 *offset
是一个眼睛。 如果命名*offsetp
它会更好看。 如果你的方法变得庞大,一般的读者不可能记得offset
是一个指针而不是一个值。 offsetp
其中p
表示“指针”将减轻将来支持您的代码的任何人的工作。
把它放在一起,我怀疑你的.write
实现,并建议你重写它。 如果还有其他错误,则需要进一步调试。 添加调试打印输出可能会派上用场,但请首先重温基本点,如空终止和缓冲区边界保护。 为了让我的答案对您有所帮助,我提供了“Linux设备驱动程序3”一书3.7节的链接,这个链接将阐明所讨论的主题。