【发布时间】:2018-04-10 20:15:31
【问题描述】:
我正在尝试编写一个可以读取、写入和查找的简单字符设备/LKM。 我在这方面遇到了很多问题,但几周来一直在努力解决它/故障排除,但一直无法让它正常工作。目前,我的模块可以正确制作并正确安装和卸载,但是如果我尝试回显到设备驱动程序文件,终端会崩溃,并且当我尝试使用 cat 从它读取时,它会返回被杀死。
本模块的步骤:
首先,我通过运行 make -C /lib/modules/$(uname -r)/build M=$PWD modules 来制作模块
对于我的内核,uname -r 是 4.10.17newkernel
我使用 sudo insmod simple_char_driver.ko 挂载模块
如果我运行 lsmod,则会列出该模块
如果我运行 dmesg,我的 init 函数“此设备现已打开”中的 KERN_ALERT 会正确触发。
此外,如果我运行 sudo rmmod,“此设备现已关闭”功能 KERN_ALERT 也会正确触发。
该模块在 cat /proc/devices 中也正确显示
我使用 sudo mknod -m 777 /dev/simple_char_driver c 240 0 在 /dev 中创建了设备驱动程序文件
在制作此文件之前,我确保 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
我使用的终端崩溃了
如果我然后重新打开一个终端,并使用:cat /dev/simple_char_driver
然后终端返回killed。
我完全不知道出了什么问题,并且我一直在寻找解决方案很长时间都没有成功。如果有人对出了什么问题有任何见解,请告诉我。
编辑:正如下面的用户所建议的,我从我的读写方法中删除了除 printk 和 return 之外的所有代码,以确保函数被触发。 然后当我使用 echo 时,dmesg 显示写入 printk 被触发,并且设备(我已经打开)关闭了。然后当我尝试 cat 设备文件时,dmesg 显示设备重新打开,“设备就绪”printk 成功显示,然后设备再次关闭。然而,echo 实际上并没有从设备文件中找到任何要读取的内容,尽管我之前已经在其中回显了“Hello world”。
编辑
最终运行的读写函数如下:
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;
}
【问题讨论】:
-
首先确保您的
.read和.write方法被实际调用。从 .write 方法中删除除printk和return length;之外的所有内容。对于.read方法 -printk和return 0;。接下来,用户空间指针只能传递给copy_to_user/copy_from_user和其他几个*_user函数。即使strlen也不应该用于用户缓冲区。 -
我按照你的建议做了。当我回显到设备并检查 dmesg 时,printk 正确显示。但是,dmesg 显示,该设备随即关闭。然后当我在设备文件上运行 cat 时,dmesg 显示设备文件已打开,触发了 printk“从设备读取”,然后设备再次关闭。设备是否应该像这样关闭/打开。另外,为什么 echo 说设备已写入,但 cat 没有在设备中找到任何可读取的内容。我该如何从这里继续前进?
-
why is it that echo said the device was written to, but cat did not find anything to read in the device.- 命令echo和cat只是做.read和.write做的事情。以某种方式实现这些文件操作取决于您,以某种方式读取和写入文件将是一致的。如您所见,实际上调用了您的.write和.read方法,请尝试将代码行一一添加到其中。这样的方式你会发现,哪条线路会出问题。
标签: linux module linux-kernel linux-device-driver device-driver