Linux Device Drivers 3 / Драйверы устройств Линукс (третье издание): 3.5. Методы open и release
| Английский оригинал | Перевод на русский | |
|---|---|---|
Сожалеем, но текст оригинала доступен только зарегистрированным пользователям. |
open и release hardorsoft 19.11.10 в 10:05 |
|
|
Теперь, после быстрого взгляда на поля, мы начнём использовать их в реальных функциях scull. hardorsoft 19.11.10 в 10:05 |
||
|
Метод open hardorsoft 19.11.10 в 10:06 |
||
|
Чтобы сделать любые инициализации при подготовке к поздним операциям для драйвера предусмотрен метод open. В большинстве драйверов open должен выполнять следующие задачи: hardorsoft 19.11.10 в 10:06 |
||
|
Проверку зависимых от устройства ошибок (таких, как "устройство не готово" или аналогичных проблем с оборудованием); hardorsoft 19.11.10 в 10:11 |
||
|
Проинициализировать устройство, если оно открывается в первый раз; hardorsoft 19.11.10 в 10:12 |
||
|
Обновить в случае необходимости указатель f_op; hardorsoft 19.11.10 в 10:12 |
||
|
Создать и заполнить любые структуры данных для размещения в filp->private_data; Первым делом, однако, обычно определяется, какое устройство открывается в настоящий момент. Вспомните прототип метода open: hardorsoft 19.11.10 в 10:12 |
||
|
int (*open)(struct inode *inode, struct file *filp); hardorsoft 29.10.10 в 23:06 |
||
|
Аргумент inode имеет информацию, которая нам необходима, в форме его поля i_cdev, в котором содержится структура cdev, которую мы создали раньше. Единственной проблемой является то, что обычно мы не хотим саму структуру cdev, мы хотим структуру scull_dev, которая содержит эту структуру cdev. Язык Си позволяет программистам использовать всевозможные трюки, чтобы выполнить такое преобразование, однако, программирование таких трюков чревато ошибками и приводит к коду, который труден для чтения и понимания другими. К счастью, в данном случае программисты ядра сделали сложную работу за нас, в виде макроса container_of, определённого в <linux></linux>kernel.h>: hardorsoft 19.11.10 в 10:12 |
||
|
container_of(pointer, container_type, container_field); hardorsoft 29.10.10 в 23:06 |
||
|
Этот макрос получает указатель на поле типа container_field, находящееся внутри структуры типа container_type, и возвращает указатель на эту структуру. В scull_open этот макрос используется, чтобы найти соответствующую структуру устройства: hardorsoft 19.11.10 в 10:13 |
||
|
struct scull_dev *dev; /* информация об устройстве */ hardorsoft 19.11.10 в 10:14 |
||
|
Как только он нашел структуру scull_dev, для облегчения доступа в будущем scull сохраняет указатель на неё в поле private_data структуры file. hardorsoft 19.11.10 в 10:14 |
||
|
Другим способом идентификации открываемого устройства является поиск младшего номера, сохранённого в структуре inode. Если вы регистрируете устройство с register_chrdev, вы должны использовать эту технику. Обязательно используйте iminor, чтобы получить младший номер из структуры inode, а также убедитесь, что он соответствует устройству, которое ваш драйвер действительно готов обслужить. hardorsoft 19.11.10 в 10:14 |
||
|
(Немного упрощённый) код для scull_open: hardorsoft 19.11.10 в 10:14 |
||
|
Код выглядит довольно разбросанным, поскольку не делает какого-либо обращения к определённому устройству, когда вызывается open. В этом нет необходимости, потому что устройство scull по дизайну является глобальным и стойким. В частности, нет никаких действий, таких, как "инициализации устройств при первом открытии", поэтому мы не храним счётчик открытий для устройств scull. hardorsoft 19.11.10 в 10:15 |
||
|
Единственная реальная операция, выполняемая на устройстве, это усечение его длины до 0, когда устройство открывается для записи. Это выполняется, так как по дизайну перезапись устройства scull более коротким файлом образует более короткую область данных устройства. Это подобно тому, как открытие обычного файла для записи обрезает его до нулевой длины. Операция ничего не делает, если устройство открыто для чтения. hardorsoft 19.11.10 в 10:15 |
||
|
Мы увидим позже, как работает настоящая инициализация, когда посмотрим на код других вариантов scull. hardorsoft 19.11.10 в 10:15 |
||
|
Метод release hardorsoft 19.11.10 в 10:15 |
||
|
Роль метода release обратна open. Иногда вы обнаружите, что реализация метода называется device_close вместо device_release. В любом случае, метод устройства должен выполнять следующие задачи: hardorsoft 19.11.10 в 10:16 |
||
|
Освободить всё, что open разместил в filp->private_data; hardorsoft 19.11.10 в 10:16 |
||
|
Выключить устройство при последнем закрытии; hardorsoft 19.11.10 в 10:16 |
||
|
Базовая форма scull не работает с аппаратурой, которую надо выключать, так что код необходим минимальный: hardorsoft 19.11.10 в 10:16 |
||
|
(* Другие разновидности устройства закрываются другими функциями, потому что scull_open заменяет различные filp->f_op для каждого устройства. Мы будем обсуждать их при знакомстве с каждой разновидностью.) hardorsoft 19.11.10 в 10:17 |
||
|
int scull_release(struct inode *inode, struct file *filp) hardorsoft 29.10.10 в 23:07 |
||
|
Вы можете быть удивлены, что происходит, когда файл устройства закрывается большее число раз, чем открывается. В конце концов, системные вызовы dup и fork создают копии открытых файлов без вызова open; каждая из этих копий затем закрывается при завершении программы. Например, большинство программ не открывают свой файл stdin (или устройство), но все они в конечном итоге его закрывают. Как драйвер узнает, когда открытый файл устройства действительно был закрыт? hardorsoft 19.11.10 в 10:17 |
||
|
Ответ прост: не каждый системный вызов close вызывает метод release. Только вызовы, которые действительно освобождают структуру данных устройства, запускают этот метод, отсюда его название. Ядро хранит счётчик, сколько раз используется структура file. Ни fork, ни dup не создают новую структуру file (только open делает это); они просто увеличивают счётчик в существующей структуре. Системный вызов call запускает метод release только тогда, когда счётчик для структуры file падает до 0, что происходит, когда структура уничтожена. Эта взаимосвязь между методом release и системным вызовом close гарантирует, что ваш драйвер видит только один вызов release для каждого open. hardorsoft 19.11.10 в 10:17 |
||
|
Обратите внимание, что метод flush вызывается каждый раз, когда приложение вызывает close. Однако, очень немногие драйверы реализуют flush, потому что обычно нечего делать во время закрытия, пока не вызван release. hardorsoft 19.11.10 в 10:17 |
||
|
Как вы можете себе представить, предыдущее обсуждение применяется даже тогда, когда приложение завершается без явного закрытия открытых файлов: ядро автоматически закрывает все файлы во время завершения процесса внутренне с помощью системного вызова close. hardorsoft 19.11.10 в 10:18 |
