Драйвер Char Linux: Какая правильная реализация операций чтения и записи file_operations? Какие проверки смещения необходимо сделать? - PullRequest
0 голосов
/ 29 февраля 2020

Я пытаюсь читать и писать в драйвер чарта. Когда я использую программу C, чтобы открыть файл устройства, прочитать и записать его, возникает ошибка SEG. Когда я использую cat для файла устройства, он становится бесконечным l oop.

1) Чего мне не хватает, и какова правильная реализация операций чтения и записи в file_operations?

2) Я знаю, что в прототипе чтение / запись: read(struct file *fp, char *ch, size_t count, loff_t *lofft) count относится к числу байтов запроса на чтение / запись. Но для чего используется последний параметр смещения и какие проверки необходимо выполнить для смещения?

3) Для нескольких вызовов чтения, таких как cat /dev/chardriver, будет ли смещение увеличиваться при каждом чтении? Подобно 1-му смещению вызова чтения от 1 до 100 для count = 100, при следующем вызове чтения смещение будет go от 101? Или это будет go от любого случайного числа?

Вот мой код:

    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/fs.h>
    #include <linux/cdev.h>
    #include <linux/device.h>
    #include <linux/uaccess.h>

    static int device_open(struct inode *, struct file *);
    static int device_release(struct inode *, struct file *);
    static ssize_t device_read(struct file *, char *, size_t, loff_t *);
    static ssize_t device_write(struct file *, const char *, size_t, loff_t *);
    char kernelbuff[1024];

    MODULE_LICENSE("GPL");

    struct file_operations fops = {
    .read = device_read,
    .write = device_write,
    .open = device_open,
    .release = device_release
    };


    int device_open(struct inode *inode, struct file *fp)
    {
    printk("device_open called");
    return 0;
    }

    static int device_release(struct inode *inode, struct file *fp)
    {
    printk("device_release called");
    return 0;
    }

    static ssize_t device_read(struct file *fp, char *ch, size_t sz, loff_t *lofft)
    {
    printk("device_read called");      
    copy_to_user(ch, kernelbuff, 1024);
    return sz;
    }

    static ssize_t device_write(struct file *fp, const char *ch, size_t sz, loff_t *lofft)
    {
    printk("device_write called");
    copy_from_user(kernelbuff, ch, 50);
    return 1024;
    }

    static int hello_init(void)
    {
      printk("basicchardriver: module initialized");
      register_chrdev(500, "chr_device", &fops);
      return 0;
    }

    static void hello_exit(void)
    {
      printk("basicchardriver: module exited");
      unregister_chrdev(500, "chr_device");
    }

    module_init(hello_init);
    module_exit(hello_exit);
 }

Для проверки:

sudo mknod -m 666 /dev/chardev c 500 0
echo "Hello World" >> /dev/chardev    ===> Works fine
cat /dev/chardev     ===> Goes to infinite loop

Если я вызываю драйвер с помощью программы C, это вызывает ошибку SEG:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>

int main()
{
  int fd;
  char buff[500];

  fd = open("/dev/chardev", O_RDWR);
  write(fd, "Hello World", 13);

  read(fd, buff, 500);
  printf("Reading data from kernel: \t");
  puts(buff);

  return 0;
}

raj@raj-VirtualBox:~/device-driver/chardriver/read-write$ ./a.out
Reading data from kernel: Hello World
*** stack smashing detected ***: <unknown> terminated
Aborted (core dumped)

1 Ответ

2 голосов
/ 29 февраля 2020

Я думаю, что получил правильный ответ на мой вопрос: (эксперты, пожалуйста, не стесняйтесь добавлять свои ответы / изменять)

Вот правильное прочтение, которое работает:

static ssize_t device_read(struct file *fp, char *ch, size_t sz, loff_t *lofft)
{
        printk("device_read called");
        if (*lofft > 1024 || sz > 1024) 
        {
          return -EFBIF; // return 0 also works
        }
        if ((*lofft+sz) > 1024)
        {
           sz = 1024 - *lofft;
        }

        copy_to_user(ch, kernelbuff + *lofft, sz);
        *lofft+=sz;
        return sz;
}

(код операций записи приведен ниже).

Ответы на смещение можно найти здесь: Понимание loff_t * offp для file_operations

Некоторые важные баллы относительно смещения:

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

  2. Следующее смещение чтения должно начинаться с last_offset + count_of_no_of_bytes_read_in_last_function_call

  3. Read должно возвращать 0, если смещение превышает размер буфера ядра.

Отредактировано: действительные проверки, необходимые для чтения, можно найти по этой ссылке (предложено @Tsyvarev): Проверка ошибок в функции '.read' в модуле ядра

Редактировать: добавление улучшенной версии функции записи

static ssize_t device_write(struct file *fp, const char *ch, size_t sz, loff_t *lofft)
{
        printk("device_write called");
        if (((*lofft) > sizeof(kernelbuff)) || (sz > sizeof(kernelbuff)))
        {
        printk("Error: Allocating more than kernel buffer size"); // pr_err( ) can also be used as pointed by @KamilCuk
        return -EFBIG;
        }
        if ((*lofft + sz) > sizeof(kernelbuff))
        {
        printk("Error: Allocating more than kernel buffer size");
        return -EFBIG;
        }

        copy_from_user(kernelbuff + *lofft, ch, sz);
        *lofft+=sz;
        return sz;
}
...