драйвер ядра для чтения ОК от пользовательского пространства, но записи всегда 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 %dn", 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 modulen");
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 modulen");
}
//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:%dn",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; }
И приложение userspace также просто:
#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 needednTo 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 opn");
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 opn");
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, я могу получить некоторые данные обратно, но, например, если я читаю с cat, это будет продолжаться бесконечно.
Я хотел бы понять, какие ошибки я совершил в своем мышлении. Есть ли способ, чтобы драйвер ядра просто выплюнул свой буфер, а затем вернул 0, чтобы мне не пришлось строить какой-то протокол там в между тем, чтобы позаботиться о том, сколько данных было прочитано и т. д.Спасибо за ваши предложения!
Edit: исправлена инструкция printk в функции memory_write и добавлена функция memory_read trace
1 ответ:
Ваша функция чтения всегда возвращает 0, потому что вы возвращаете
retval
, а не количество прочитанных байтов. Пока вызовcopy_to_user()
всегда успешен,retval
всегда будет 0. Вместо этого, покаcopy_to_user()
успешно выполняется, вы должны вернуть количество байтов, фактически записанных в пользовательское пространство. эта документация утверждает, чтоcopy_to_user()
возвращает общее число байтов, которые не удалось скопировать.В качестве отступления вы игнорируете значение count. Очень возможно, что пользователь является запрашивать меньше данных, чем имеется в вашем буфере. Вы никогда не должны игнорировать графа.
Теперь у вас есть проблема, когда ваша функция никогда не возвращает 0. Возврат 0 важен, потому что is сообщает пользовательскому приложению, что больше нет данных, доступных для чтения, и пользовательское приложение должно закрыть файл устройства.
Вам нужно отслеживать в драйвере, сколько байтов было прочитано и сколько байтов было записано. Это может быть реализовано используя Ваш
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; } }