Понимание loff_t * offp для file_operations - PullRequest
18 голосов
/ 15 марта 2012

Я разрабатываю драйвер устройства, который просто читает и записывает в символьный буфер.Мой вопрос, однако, касается двух функций в структуре file_operations read и write.Я не совсем понимаю, что на самом деле loff_t *offp.Я знаю, что для операций чтения и записи *offp - это смещение файла, означающее текущую позицию чтения / записи файла, однако я даже не уверен, что означает запись или чтение в / из файла устройства.

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

struct my_char_structure{
    struct cdev my_cdev;
    struct semaphore sem;
    char *data;
    ssize_t data_size;
    unsigned int access_key;
    unsigned long size;
};

Это статическая структура, которая инициализируется и на которую указывает мой драйвер insmod как таковой.

static dev_t dev_num;
static struct my_char_structure Dev;

int start_mod(void){
    //Because we are dealing with a fictitious device, I want
    //the driver to create my two devices with arbitrarily 
    //assigned major numbers.
    struct my_char_structure *my_dev = &Dev;
    int err;

    alloc_chrdev_region(&dev_num, FIRST_MINOR, COUNT, DEVICE_NAME);

    sema_init(&(my_dev->sem),1);

    cdev_init(&(my_dev->my_cdev), &fops);
    my_dev->my_cdev.owner = THIS_MODULE;
    my_dev->my_cdev.ops = &fops;// fops is my file operations struct

    err = cdev_add(&my_dev->my_cdev, dev_num, COUNT);
    if(err<0)
        printk(KERN_ALERT "There was an error %d.",err);
    printk(KERN_ALERT " insmod to major number %d",MAJOR(dev_num));

    return 0;   
}

module_init(start_mod);

Когда мое устройство открыто, я просто создаю указатель для открытого файла, чтобы указать на ту статическую структуру, которую я настроил во время module_init(start_mod) как таковой ...

int dev_open(struct inode *in_node, struct file *filp){
    static struct my_char_structure *my_dev;
    my_dev = container_of(in_node->i_cdev, struct my_char_structure, my_cdev);
    printk(KERN_ALERT "The device number is %d",iminor(in_node));
    if(!my_dev)
        printk(KERN_ALERT "something didn't work. my_dev not initialized.");
    filp->private_data = my_dev;
    return 0;
}

Мои методы чтения и записи изменяют исходную структуру Dev, на которую я указал в моих открытых файлах.Все, что я copy_to_user из моей структуры, - это то, что пользователь считает записанным на устройство, и все, что я copy_from_user думает, что пользователь пишет.Но помимо изменения моей исходной структуры Dev, идея положения или смещения файла не имеет смысла, если она не ссылается на указатель на буферизованную память в ядре для какой-либо произвольной структуры или типа.Это единственная интерпретация, которую я имею для смещения файла ... это правильно?Это то, к чему здесь относится loff_t *offp?

write(struct file *filp, const char __user *buff, size_t count, loff_t *offp)
read(struct file *filp, char __user *buff, size_t count, loff_t *offp)

(если я правильно понял) Когда вызывается какая-то операция file_, такая как чтение / запись, и я не установил *offp лично, чтоloff_t * offp изначально установлен в?

Если в последнем file_operation offp = some_arbitrary_address (потому что я так сказал), это то, что будет установлено offp, когда эта операция будет вызвана снова?

Что произойдет, если у меня будут запущены другие операции file_opens, будет ли он установлен на то, что оставила для него последняя операция file_operation, или будет хранить вкладку, какую операцию file_open он использовал, и заменит * offp на то, что было в file_openв?

Концепция устройства char слишком абстрактна для меня, когда кажется, что само устройство даже не хранит информацию, как файл, а скорее это драйвер, который сохраняет информацию.Надеюсь, я объяснил свою туманность и проясню все, что мне кажется неоднозначным.

Ответы [ 2 ]

20 голосов
/ 15 марта 2012

«loff_t» - это «длинное смещение», то есть позиция поиска, которая объединяет безумное разнообразие off_t, off64_t и т. Д., Так что драйверы могут просто использовать loff_t и не беспокоиться об этом.

Сам указатель, когда вы входите в драйвер, указывает на смещение, предоставленное пользователем (при условии, что это код пользователя, выполняющий доступ к драйверу - технически ядро ​​может предоставить свое собственное, но случай пользователя - этоподумать) через lseek или llseek или lseek64 и т. д., а затем обычными операциями чтения и записи.Рассмотрим случай с обычным файлом на диске: когда вы впервые open файл, вы (как пользователь) получаете ядро ​​для предоставления структуры данных, которая отслеживает вашу текущую позицию в файле, так что если вы read или write некоторых байтов, следующий read или write будет получен с того места, где вы остановились.

Кроме того, если вы dup дескриптор файла или сделаете эквивалент с помощью (например,) fork и exec с точки зрения выполнения последовательности команд, эта позиция поиска является общей для всех наследующих процессов.Следовательно, в приглашении оболочки команда:

(prog1; prog2; prog3) > outputfile

создает выходной файл, затем dup s дескриптор для трех программ, так что вывод, который записывает prog2, поступает в файл сразу послевыходные данные из prog1 и выходные данные из prog3 следуют за двумя другими - все потому, что все три отдельных процесса совместно используют одну и ту же базовую структуру данных ядра с одинаковой внутренней loff_t.

То же самое относится к устройствуфайлы драйверов.Когда вы вызываете ваши функции чтения и записи, вы получаете «текущее смещение», предоставленное пользователем, и вы можете (и должны) обновлять его по мере необходимости ... при условии, что есть какая-то необходимость (например, вы хотите предоставить пользователямвнешний вид обычного файла, в том числе тот факт, что смещения поиска перемещаются при чтении и записи).Если у устройства есть какое-то логическое применение смещения поиска, вы можете использовать его здесь.

Конечно, есть намного больше драйверов устройств, поэтому в этом материале есть целые главы книг (см.),: -)

0 голосов
/ 19 декабря 2017

Ответ Торека отлично. Просто добавив немного дополнительных деталей / контекста ... Из более раннего ядра Linux (2.6.28), вот пример смещения, используемого в системном вызове ... он копирует смещение из пространства пользователя во временную переменную перед получением в механизм вызова драйвера ядра, а затем копирует его обратно в файл пользователя. Таким образом, смещение, которое видит драйвер, отделено от его пользовательского представления и облегчает ситуации, когда смещение NULL в системном вызове, так что SEGVIO не происходит.

SYSCALL_DEFINE4(sendfile64, int, out_fd, int, in_fd, loff_t __user *, offset, size_t, count)
{
    loff_t pos;
    ssize_t ret;

    if (offset) {
        if (unlikely(copy_from_user(&pos, offset, sizeof(loff_t))))
            return -EFAULT;
        ret = do_sendfile(out_fd, in_fd, &pos, count, 0);
        if (unlikely(put_user(pos, offset)))
            return -EFAULT;
        return ret;
    }

    return do_sendfile(out_fd, in_fd, NULL, count, 0);
}
...