内核驱动程序从用户空间读取正常,但回写始终为0 [英] kernel driver reading ok from user space, but writing back is always 0

查看:113
本文介绍了内核驱动程序从用户空间读取正常,但回写始终为0的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

因此,我正在通过内核驱动程序编程进行工作,目前,我正在尝试在应用程序和内核驱动程序之间建立简单的数据传输.

So I'm working my way through kernel driver programming, and currently I'm trying to build a simple data transfer between application and kernel driver.

我正在使用简单字符设备作为两者之间的链接,并且我已成功将数据传输到驱动程序,但是我无法将有意义的数据返回给用户空间.

I am using simple character device as a link between these two, and I have succeeded to transfer data to driver, but I can't get meaningful data back to user space.

内核驱动程序如下:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h> /* printk() */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include <linux/proc_fs.h>
#include <asm/uaccess.h> /* copy_from/to_user */

MODULE_LICENSE("GPL");

//Declarations
int memory_open(struct inode *inode, struct file *filp);
int memory_release(struct inode *inode, struct file *filp);
ssize_t memory_read(struct file *filp, char *buf, size_t count, loff_t *f_pos);
ssize_t memory_write(struct file *filp, char *buf, size_t count, loff_t *f_pos);
void memory_exit(void);
int memory_init(void);

/* Structure that declares the usual file access functions */
struct file_operations memory_fops = {
    read: memory_read,
    write: memory_write,
    open: memory_open,
    release: memory_release
};

//Default functions
module_init(memory_init);
module_exit(memory_exit);

/* Global variables of the driver */
/* Major number */
int memory_major = 60;
/* Buffer to store data */
char* tx_buffer;
char* rx_buffer;

int BUFFER_SIZE=64;
int actual_rx_size=0;

int memory_init(void) {
    int result;

    /* Registering device */
    result = register_chrdev(memory_major, "move_data", &memory_fops);
    if (result < 0) {
        printk(
        "<1>move_data: cannot obtain major number %d\n", memory_major);
        return result;
    }

    /* Allocating memory for the buffers */
    //Allocate buffers
    tx_buffer = kmalloc(BUFFER_SIZE,  GFP_KERNEL);
    rx_buffer = kmalloc(BUFFER_SIZE,  GFP_KERNEL);

    //Check allocation was ok
    if (!tx_buffer || !rx_buffer) {
        result = -ENOMEM;
        goto fail;
    }

    //Reset the buffers
    memset(tx_buffer,0, BUFFER_SIZE);
    memset(rx_buffer,0, BUFFER_SIZE);

    printk("<1>Inserting memory module\n"); 
    return 0;

    fail:
        memory_exit(); 
        return result;
}

void memory_exit(void) {
    /* Freeing the major number */
    unregister_chrdev(memory_major, "memory");

    /* Freeing buffers */
    if (tx_buffer) {
        kfree(tx_buffer); //Note kfree
    }

    if (rx_buffer) {
        kfree(rx_buffer); //Note kfree
    }
    printk("<1>Removing memory module\n");
}


//Read function
ssize_t memory_read(struct file *filp, char *buf, size_t count, loff_t *f_pos) { 

    printk("user requesting data, our buffer has (%d) \n", actual_rx_size);

    /* Transfering data to user space */ 
    int retval = copy_to_user(buf,rx_buffer,actual_rx_size);

    printk("copy_to_user returned (%d)", retval);

    return retval;
}

ssize_t memory_write( struct file *filp, char *buf,
                  size_t count, loff_t *f_pos) {

    //zero the input buffer
    memset(tx_buffer,0,BUFFER_SIZE);
    memset(rx_buffer,0,BUFFER_SIZE);

    printk("New message from userspace - count:%d\n",count);

    int retval = copy_from_user(tx_buffer,buf,count);

    printk("copy_from_user returned (%d) we read [%s]\n",retval , tx_buffer);
    printk("initialize rx buffer..\n");

    memcpy(rx_buffer,tx_buffer, count);
    printk("content of rx buffer [%s]\n", rx_buffer);

    actual_rx_size = count;

    return count; //inform that we read all (fixme?)
}

//Always successfull
int memory_open(struct inode *inode, struct file *filp) { return 0; }
int memory_release(struct inode *inode, struct file *filp) { return 0; } 

用户空间应用程序也很简单:

And the userspace application is simple as well:

#include <unistd.h>     //open, close | always first, defines compliance
#include <fcntl.h>      //O_RDONLY
#include <stdio.h>
#include <stdlib.h>     //printf
#include <string.h>

int main(int args, char *argv[])
{
int BUFFER_SIZE = 20;

char internal_buf[BUFFER_SIZE];
int to_read = 0;

memset(internal_buf,0,BUFFER_SIZE);

if (args < 3) {
    printf("2 Input arguments needed\nTo read 10 bytes: \"%s read 10\" \
    \nTo write string \"hello\": \"%s write hello\"\nExiting..\n", argv[0], argv[0]);
    return 1;
}


//Check the operation
if (strcmp(argv[1],"write") == 0) {

    printf("input lenght:%d", strlen(argv[2]));
    //Make sure our write fits to the internal buffer
    if(strlen(argv[2]) >= BUFFER_SIZE) {
        printf("too long input string, max buffer[%d]\nExiting..", BUFFER_SIZE);
        return 2;
    }

    printf("write op\n");
    memcpy(internal_buf,argv[2], strlen(argv[2]));

    printf("Writing [%s]\n", internal_buf);

    FILE * filepointer;
    filepointer = fopen("/dev/move_data", "w");
    fwrite(internal_buf, sizeof(char) , strlen(argv[2]), filepointer);
    fclose(filepointer);

} else if (strcmp(argv[1],"read") == 0) {
    printf("read op\n");

    to_read = atoi(argv[2]);

    FILE * filepointer;
    filepointer = fopen("/dev/move_data", "r");
    int retval = fread(internal_buf, sizeof(char) , to_read, filepointer);
    fclose(filepointer);

    printf("Read %d bytes from driver string[%s]\n", retval, internal_buf);
} else {
    printf("first argument has to be 'read' or 'write'\nExiting..\n");
    return 1;
}


return 0;
}

执行我的应用程序时,会发生以下情况:

When I execute my application, this is what happens:

./rw write "testing testing"

kernel side:
[ 2696.607586] New message from userspace - count:15
[ 2696.607591] copy_from_user returned (0) we read [testing testing]
[ 2696.607593] initialize rx buffer..
[ 2696.607594] content of rx buffer [testing testing]

所以一切看起来都正确.但是当我尝试阅读时:

So all look correct. But when I try to read:

./rw read 15
read op
Read 0 bytes from driver string[]

Kernel 
[  617.096521] user requesting data, our buffer has (15) 
[  575.797668] copy_to_user returned (0)
[  617.096528] copy_to_user returned (0)

我想我做错了很简单,因为如果我不返回0,我可以取回一些数据,但是例如,如果我用cat读取,它将不断循环.

I guess it's quite simple what I'm doing wrong, since if I don't return 0, I can get some data back, but for example if I read with cat, it will continue looping endlessly.

我想了解自己在思考中犯了什么错误. 有没有一种方法,内核驱动程序会吐出它的缓冲区,然后返回0,这样我就不必在两者之间建立一些协议来照顾已经读取了多少数据等.

I would like to understand what mistakes I have made in my thinking. Is there a way that kernel driver would just spit out it's buffer, and then return 0, so that I wouldn't have to build some protocol there in between to take care of how much data has been read etc.

感谢您的建议!

更正了memory_write函数中的printk语句,并添加了memory_read函数跟踪

corrected the printk statement in memory_write function, and added memory_read function trace

推荐答案

您的读取函数始终返回0,因为您正在返回retval,而不是读取的字节数.只要copy_to_user()调用始终成功,retval始终将为0.相反,只要copy_to_user()成功,您应该返回实际写入用户空间的字节数. 本文档指出,copy_to_user()返回其字节总数.无法复制.

Your read function always returns 0 because you are returning retval, and not the count of bytes read. As long as the copy_to_user() call always succeeds, retval will always be 0. Instead, as long as copy_to_user() succeeds, you should return the number of bytes actually written to user space. This documentation states that copy_to_user() returns the total number of bytes that it was unable to copy.

顺便说一句,您忽略了count的值.用户请求的数据很有可能比缓冲区中可用的数据少.您永远都不要忽略计数.

As an aside, you are ignoring the value of count. It is very possible that the user is requesting less data than you have available in your buffer. You should never ignore count.

现在您遇到了函数从不返回0的问题.返回0很重要,因为它告诉用户应用程序没有更多数据可读取,并且用户应用程序应关闭该窗口.设备文件.

Now you have the problem where your function never returns a 0. Returning a 0 is important because is tells the user application that there is no more data available for reading and the user application should close the device file.

您需要在驱动程序中跟踪已读取的字节数与已写入的字节数.可以使用您的actual_rx_size来实现.

You need to keep track in your driver how many bytes have been read vs. how many bytes have been written. This may be implemented using your actual_rx_size.

尝试一下:

//Read function
ssize_t memory_read(struct file *filp, char *buf, size_t count, loff_t *f_pos) { 

    ssize_t bytes;

    if (actual_rx_size < count)
        bytes = actual_rx_size;
    else
        bytes = count;

    printk("user requesting data, our buffer has (%d) \n", actual_rx_size);

    /* Check to see if there is data to transfer */
    if (bytes == 0)
        return 0;

    /* Transfering data to user space */ 
    int retval = copy_to_user(buf,rx_buffer,bytes);

    if (retval) {
        printk("copy_to_user() could not copy %d bytes.\n", retval);
        return -EFAULT;
    } else {
        printk("copy_to_user() succeeded!\n");
        actual_rx_size -= bytes;
        return bytes;
    }
}

这篇关于内核驱动程序从用户空间读取正常,但回写始终为0的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆