Ответ 1
Резьбонарезную безопасность! Что делать, если два потока/процессы используют драйвер одновременно?
Я изучаю главу 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 */
}
Мне это кажется прекрасным, пока я не понял, что во время инициализации в scull_setup_cdev
здесь уже был struct scull_dev *dev
.
Я довольно смущен, так как я думал, что мы можем сделать struct scull_dev *dev
глобальную переменную, тогда scull_read
и другие методы в конечном итоге будут иметь к ней доступ, не пройдя все прохождение с помощью inode
и file
.
Мой вопрос: почему бы нам просто не сделать глобальную переменную?
Может ли кто-нибудь предоставить некоторые практические примеры использования этого метода для передачи данных?
Резьбонарезную безопасность! Что делать, если два потока/процессы используют драйвер одновременно?
Основная причина заключается в том, что ваш драйвер может управлять несколькими устройствами. Например, вы можете создать (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. Если вы вникнете в код, вы можете увидеть открытые, читать, записать все используемые down_interruptible().
Я думаю, автор 1) считает, что доступ к scull_dev напрямую не выглядит хорошо 2) хочет показать нам, как использовать private_data. Помещая точку в scull_dev в файл структуры, указатель которого отправляется каждой операции, каждая операция может получить к ней доступ без использования глобальной переменной.
Драйвер scull реализован с 4-мя несовершеннолетними, каждый из которых имеет отдельный scull_dev, каждый scull_dev имеет встроенную в него "struct cdev". Теперь предположим, что пользователь открыл scull0 из /dev/scull 0. В функции open() вам нужно указать правильную структуру scull_dev. Структуры scull_dev динамически распределяются.
Вы можете увидеть полную реализацию здесь https://github.com/mharsch/ldd3-samples/blob/master/scull/main.c#L450