Бесконечное циклирование при чтении с символьного устройства

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

Кажется, это должно быть довольно прямолинейно. Просто используйте copy_to_user(), но это оказалось очень проблематичным.

В любом случае, вот код. Я думаю, проблема в функции gdev_read(). Printk должен использоваться как отладка, так и точки разговора, так как я должен представить проект в классе.

/*
 * Implement a generic character pseudo-device driver
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/types.h>
#include <linux/vmalloc.h>
#include <asm/uaccess.h>

/* you need these, or the kernel will be tainted */
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("A simple sample character device driver");

/*
 * function prototypes
 */
int init_module(void);
void cleanup_module(void);
static ssize_t gdev_read(struct file *, char *, size_t, loff_t *);
static ssize_t gdev_write(struct file *, const char *, size_t, loff_t *);
static int gdev_open(struct inode *, struct file *);
static int gdev_release(struct inode *, struct file *);

/* macros */
#define TRUE 1
#define FALSE 0
#define MAX_MSG_LEN 64

/*
 * global variables
 */
static dev_t dev_num;   /* device number, for new device */
static char *mesg;  /* buffer for message */


/* file operations structure, so my device knows how to act */
static struct file_operations fops = {
    .owner =    THIS_MODULE,
    .read =     gdev_read,
    .write =    gdev_write,
    .open =     gdev_open,
    .release =  gdev_release,
};

/* character device struct.  Declaired here, but initialized elsewhere */
struct cdev *gdev;

int init_module(void)
{   
    int err;
    printk(KERN_ALERT "in init_module\n");

    if(alloc_chrdev_region(&dev_num, 0, 1, "/dev/gdev")){
        printk(KERN_INFO "Could not allocate device numbers\n");
        printk(KERN_INFO "Module gdev not loaded\n");
        return -1;
    }

    /* now I need to make the device and register it */
    gdev = cdev_alloc();
    gdev->owner = THIS_MODULE;
    gdev->ops = &fops;
    err = cdev_add(gdev, dev_num, 1);
    if(err){
        printk(KERN_NOTICE "Error %d adding gdev", err);
        return err;
    }

    mesg = (char *)vmalloc(MAX_MSG_LEN);

    printk(KERN_INFO "Module gdev successfully loaded.\n");
    printk(KERN_INFO "gdev Major Number: %d\n", MAJOR(dev_num));

    return 0;
}


void cleanup_module(void)
{
    printk(KERN_ALERT "in cleanup_module\n");
    unregister_chrdev_region(dev_num, 3);
    vfree(mesg);
    cdev_del( gdev );
    printk(KERN_INFO "Module gdev unregistered\n");
}

static ssize_t gdev_read(struct file *filp, char *page, 
            size_t len, loff_t *offset)
{
    ssize_t bytes = len < MAX_MSG_LEN ? len : MAX_MSG_LEN;
    printk(KERN_ALERT "in gdev_read\n");
    if(copy_to_user(page, mesg, bytes)){
        return -EFAULT;
    }
    return bytes;
}

static ssize_t gdev_write(struct file *filp, const char *page, 
            size_t len, loff_t *offset)
{
    ssize_t bytes = len < MAX_MSG_LEN ? len : MAX_MSG_LEN; 
    printk(KERN_ALERT "in gdev_write\n");
    if(copy_from_user(mesg, page, bytes)){
        return -EFAULT;
    }

    return bytes;
}

static int gdev_open(struct inode *inode, struct file *filp)
{
    printk(KERN_ALERT "in gdev_open\n");
    return 0;
}

static int gdev_release(struct inode *inode, struct file *filp)
{
    printk(KERN_ALERT "in gdev_release\n");
    /* doesn't do anything because it doesn't need too */
    return 0;
}

Ответ 1

Если ноль не возвращается из read() (в вашем случае gdev_read()), функция read будет вызвана снова. Чтобы остановить это, вы используете параметр loff_t *offset. Увеличьте его на сколько байтов вы прочитали с помощью (*offset) += bytes; после copy_to_user(). В следующий раз вызывается read(), offset будет тем, к чему вы его увеличили. Теперь просто проверьте, сколько байтов вы уже отправили, и отправляйте только то, что у вас осталось. Ваша функция должна выглядеть так:

static ssize_t gdev_read(struct file *filp, char *page, 
            size_t len, loff_t *offset)
{
    ssize_t bytes = len < (MAX_MSG_LEN-(*offset)) ? len : (MAX_MSG_LEN-(*offset));
    printk(KERN_ALERT "in gdev_read\n");
    if(copy_to_user(page, mesg, bytes)){
        return -EFAULT;
    }
    (*offset) += bytes;
    return bytes;
}

Ответ 2

Вы можете использовать 'simple_read_from_buffer' функцию из 'linux/fs.h':

static ssize_t gdev_read(struct file *filep, char __user *buff, size_t count, loff_t *offp)
{
    return simple_read_from_buffer(buff, count, offp, my_buffer, buffer_len);
}

' my_buffer и buffer_len' определены в вашем модуле.