Причина передачи данных с использованием struct inode и struct file в программировании драйверов устройств Linux

я изучаю главу 3.5 драйверы устройств Linux, 3-е издание. В этом разделе представлен метод извлечения пользовательской структуры, которую мы определили сами из struct inode *inode в открытой функции:

int scull_open(struct inode *inode, struct file *filp)
{
    struct scull_dev *dev;

    dev = container_of(inode->i_cdev, struct scull_dev, cdev);
    filp->private_data = dev; /* for other methods */

    }
    return 0;          
}

из моего понимания, пока устройство открыто,struct inode *inode представление устройства передается в scull_open. Затем пользовательская структура dev извлекается и передается в filp->private_data так что другие методы, такие как scull_read можно использовать это:

ssize_t scull_read(struct file *filp, char _ _user *buf, size_t count,
                loff_t *f_pos)
{
    struct scull_dev *dev = filp->private_data; 
    /* other codes that uses *dev   */
}

мне это кажется прекрасным, пока я не понял, что у нас уже есть struct scull_dev *dev во время инициализации в scull_setup_cdev здесь.

я немного смущен, так как я думал, что мы можем сделать struct scull_dev *dev глобальная переменная, то scull_read и другие методы в конечном итоге будут иметь доступ к нему, не проходя через все прохождения с помощью inode и file.

мой вопрос в том, почему бы нам просто не сделать его глобальной переменной?

Can кто-нибудь приводит практические примеры использования этого метода для передачи данных ?

5 ответов


потокобезопасность! Что делать, если два потока/процесса используют драйвер одновременно?


основная причина заключается в том, что ваш драйвер может управлять более чем одним устройством. Например, вы можете создать (mknod) несколько устройств /dev/scull1, /dev/scull2, /dev/scull3... и тогда у каждого из них будет свой scull_dev связанные с ним.

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


вы также можете избежать использования личных данных для хранения фактического устройства, что является общим выбором, если вам нужны личные данные для чего-то другого. В этом случае вам нужно будет получить незначительное число в процедуре scull_read. Это будет что-то вроде этого:

ssize_t scull_read( struct file *filp,
                     char __user* buf,
                     size_t count,
                    loff_t * f_pos ) {

    int minor = MINOR(filp->f_dentry->d_inode->i_rdev);
    printk( "reading on minor number %d\n", minor);
    /* use dev[minor] in ur code */
    return 0;
 }

Я не думаю,что это проблема безопасности. Это больше похоже на выбор дизайна. Если я не ошибаюсь, потокобезопасность достигается и семафора в scull_dev. Если вы покопаетесь в коде, вы можете увидеть open, read, write all used down_interruptible ().

Я думаю, автор 1) считает, что доступ к scull_dev напрямую не выглядит хорошо 2) хочет показать нам, как использовать private_data. Поставив точку в scull_dev в файле структуры, указатель на который отправляется на каждую операцию, каждый операция может получить к нему доступ без использования глобальной переменной.


драйвер scull реализован с 4 несовершеннолетними, каждый из которых имеет отдельный scull_dev, каждый scull_dev имеет встроенную в него "структуру cdev". Теперь предположим, что пользователь открыл scull0 из /dev/scull0. В функции open () вам нужно указать на правильную структуру scull_dev. Структуры scull_dev динамически распределяются.

вы можете увидеть полную реализацию здесь https://github.com/mharsch/ldd3-samples/blob/master/scull/main.c#L450