драйвер ядра нормально читает из пользовательского пространства, но обратная запись всегда 0

Итак, я работаю над программированием драйвера ядра, и в настоящее время я пытаюсь создать простую передачу данных между приложением и драйвером ядра.

Я использую простое символьное устройство в качестве связующего звена между этими двумя, и мне удалось передать данные драйверу, но я не могу вернуть значимые данные обратно в пространство пользователя.

Драйвер ядра выглядит так:

#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; } 

И пользовательское приложение тоже простое:

#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;
}

Когда я запускаю свое приложение, происходит следующее:

./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]

Так что все выглядит правильно. Но когда я пытаюсь прочитать:

./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, я могу получить некоторые данные обратно, но, например, если я читаю с кошкой, это будет продолжаться бесконечно.

Я хотел бы понять, какие ошибки я допустил в своем мышлении. Есть ли способ, которым драйвер ядра просто выплюнет свой буфер, а затем вернет 0, чтобы мне не пришлось создавать какой-то протокол между ними, чтобы заботиться о том, сколько данных было прочитано и т. д.

Спасибо за ваши предложения!

Изменить: исправлен оператор printk в функции memory_write и добавлена ​​трассировка функции memory_read.


person julumme    schedule 07.06.2013    source источник


Ответы (1)


Ваша функция чтения всегда возвращает 0, потому что вы возвращаете retval, а не количество прочитанных байтов. Пока вызов copy_to_user() всегда успешен, retval всегда будет равен 0. Вместо этого, пока copy_to_user() успешен, вы должны возвращать количество байтов, фактически записанных в пользовательское пространство. В этой документации указано, что copy_to_user() возвращает общее количество байтов, которое не удалось копировать.

Кроме того, вы игнорируете значение count. Вполне возможно, что пользователь запрашивает меньше данных, чем доступно в вашем буфере. Вы никогда не должны игнорировать счет.

Теперь у вас есть проблема, когда ваша функция никогда не возвращает 0. Возврат 0 важен, потому что он сообщает пользовательскому приложению, что больше нет данных, доступных для чтения, и пользовательское приложение должно закрыть файл устройства.

Вам нужно отслеживать в драйвере, сколько байтов было прочитано и сколько байтов было записано. Это может быть реализовано с помощью вашего 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;
    }
}
person Benjamin Leinweber    schedule 08.06.2013
comment
Бенджамин, ты был прав насчет денег. Отслеживание того, что было отправлено, было ключом к этому. Отличное решение, и работает именно так, как я надеялся! Спасибо! Пс. последний оператор printk отсутствует );. - person julumme; 09.06.2013
comment
Ой, извините за это. У меня дома нет системы разработки, поэтому у меня не было возможности ее скомпилировать. Исправлено сейчас. - person Benjamin Leinweber; 09.06.2013
comment
Спасибо @BenjaminLeinweber. У меня была проблема с функцией записи. Возврат 0 каким-то образом отправил ее в бесконечный цикл. Теперь я знаю, почему :) - person SteveIrwin; 04.10.2013
comment
@SteveIrwin - Почему возврат нуля приводит к циклу? У меня тоже есть эта проблема. - person aditya; 27.12.2013
comment
В ответе четко указано, что каждый раз, когда вам нужно возвращать количество прочитанных байтов, иначе он попадет в бесконечный цикл. Если вы опубликуете свой код, мы сможем отследить виновника. - person SteveIrwin; 28.12.2013