Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология Образование Политология Производство Психология Стандартизация Технологии |
Выделение очереди из памяти ядра и обнуление ее содержимого.
Строка 1406 Инициализация списка запросов, содержащего очередь чтения и очередь записи. Строка 1414 Связь выбранного листа с очередью и его инициализация. Строки 1417-1424 Связь лифтозависимых функций с данной очередью. Строка 1426 Эта функция устанавливает границу объединяемого сегмента и проверяет, чтобы он был не меньше минимального размера. Строка 1428 Эта функция устанавливает функцию, используемую при изъятии запроса из очереди драйвером. Позволяет использовать для обработки очереди альтернативную функцию. Строка 1429 Инициализирует верхний предел размера комбинируемых сегментов. Строка 1431 Инициализация максимального количества сегментов, которые может обработать драйвер. Строка 1432 Инициализация максимального количества физических сегментов за один запрос. Значения для строк 1429-1432 устанавливаются в include/linux/kerne 1.h. Строка 1434 Возвращение инициализированной очереди. Строки 1435-1439 Вспомогательная функция для очистки памяти в случае возникновения ошибки. Теперь наши запросы находятся на своих местах и инициализированы. Устройства Перед тем как мы рассмотрим слой обобщенного устройства и обобщенный блочный драйвер, давайте коротко пройдемся по программному слою и посмотрим на манипуляции с вводом-выводом блочного устройства (см. рис. 5.4). На уровне приложения приложение инициализирует файловую операцию с помощью f read (). При вызове f read () этот запрос передается в слой виртуальной файловой системы (VFS) (описываемой в гл. 41), где хранится структура файла dentry, и через структуру inode. Слой VFS пытается найти запрашиваемую страницу в буфере кеша, и, если она там отсутствует вызывается обработчик файловой системы (filesystem handler) для получения требуемого физического блока; inode связан с обработчиком файловой системы, который связан с соответствующей файловой системой. Обработчик файловой системы вызывает утилиты очереди запросов (request queue utilites), являющиеся частью слоя обобщенного блочного устройства (generic block device layer), для создания корректного запроса для физических блока и устройства. Запрос помещается в очередь запросов, поддерживаемую слоем обобщенного блочного устройства. Пример: «обобщенное» блочное устройство Рассмотрим слой обобщенного блочного устройства. В соответствии с рис. 5.4 он находится выше слоя физического устройства и сразу под слоем файловой системы. Основная задача слоя обобщенного устройства - это поддержание очереди запросов и связанные с ней операции. Сначала мы регистрируем наше устройство с помощью register_blkdev (maj or, dev_name, fops). Эта функция получает старший номер запроса, имя блочного устройства (появляющееся в директории /dev) и указатель на структуру файловой операции. В случае удачи возвращается желаемый старший номер. Далее мы создаем структуру gendisk. Функция alloc__disk( in t minors) в include/linux/genhd. h получает номер раздела и возвращает указатель на структуру gendisk. Теперь посмотрим на структуру gendisk: include/linux/genhd.h 081 struct gendisk { 82 int major; /* старший номер драйвера */ 83 int first_minor; 84 int minors; 85 char disk_name[16]; /* имя старшего драйвера */ 86 struct hd_struct **part; /* [индекс младшего] */ 87 struct block_device_operations *fops; 88 struct request_queue *queue; 89 void *private_data; В гл. 6 (опечатка в оригинале). Примеч. науч. ред. Глава 5* Ввод-Вывод 090 sector_t capacity; 091 92 int flags; 93 char devfs_name[64]; /* devfs crap */ 94 int number; /* еще то же самое */ 95 struct device *driverfs_dev; 96 struct kobject kob j; 097
98 struct timer__rand_state * random; 99 int policy; 100
101 unsigned sync_io; /* RAID */ 102 unsigned long stamp, stamp_idle; 103 int in_flight; 104 #ifdef CONFIG_SMP 105 struct disk__stats *dkstats; 106 #else 107 struct disk_stats dkstats; 108 #endif 109 }; Строка 82 Поле ma j or__num заполняется на основе результата regis ter_blkdev (). Строка 83 Блочное устройство для жесткого диска может обрабатывать несколько физических устройств. Несмотря на то что это зависит от драйвера, младший номер обычно соответствует каждому физическому приводу. Поле f irst_minor является первым физическим устройством. Строка 85 Имя disk__name, такое, как hda или sdb, является именем всего диска. (Разделы диска именуются hdal, hda2 и т. д. Они являются логическими дисками внутри физического диска.) Строка 87 Поле fops в block_device_operations инициализирует структуру файловой операции. Структура файловой операции содержит указатель на вспомогательную функцию в низкоуровневом драйвере устройства. Эти функции являются драйверо-зависимыми и не обязательно реализованы во всех драйверах. Обычно реализуются файловые операции open, close, read и write. В гл. 4, «Управление памятью»1 обсуждается структура файловой операции. Очевидно, имеется в виду гл. 6, «Файловые системы». Примеч. науч. ред. Устройства Строка 88 Поле queue указывает на список запрашиваемых операций, которые должен выполнить драйвер. (Мы еще обсудим инициализацию очереди запросов.) Строка 89 Поле private_data хранит драйверозависимые данные. Строка 90 Поле capacity устанавливается в соответствии с размером устройства (в секторах по 512 кб). Вызов set_capacity () должен получать это значение. Строка 92 Поле flags означает атрибуты устройства. В случае дискового привода это тип носителя, т. е. CD, съемный привод и т. д. Теперь мы посмотрим, с чем связана инициализация очереди запросов. Когда очередь запросов уже определена, мы вызываем blk_init_queue (request_fn_proc, spinlock__t). Эта функция получает в качестве первого параметра функцию передачи, которая будет вызываться в интересах файловой системы. Функция blk_init_ queue () выделяет очередь с помощью blk__alloc__queue () и затем инициализирует структуру очереди. Второй параметр, blk_init__queue (), - это связанная с очередью блокировка всех операций. И наконец, для того чтобы сделать блочное устройство видимым для ядра, драйвер должен вызвать add__disk(): Drivers/block/genhd.с 193 void add_disk(struct gendisk *disk) 194 {
195 disk-> flags |= GENHD_FL_UP; 196 blk_register_region(MKDEV(disk-> major, disk-> f irst_minor), 197 disk-> minors, NULL, exact_match, exact_lock, disk); 198 register_disk(disk); 199 blk_register_queue(disk); 200 } Строка 196 Устройство отображается в ядро на основе своего размера и количества разделов. Вызов blk_register__region () имеет несколько параметров: 1. В этот параметр упакованы старший номер диска и первый младший номер. 2. Это диапазон младших номеров, следующих за первым (если этот драйвер обрабатывает несколько младших номеров). Глава 5 • Ввод-Вывод 3. Это загружаемый модуль, содержащий драйвер (если он есть). 4. exact__match - это функция для поиска соответствующего диска. 5. exact_lock - это функция блокировки кода, после того как exact_match найдет нужный диск. 6. disk - это обработчик, используемый ехасt_match и exact_lock для идентификации нужного диска. Строка 198 register__disk проверяет раздел и добавляет его в файловую систему. Строка 199 Регистрация очереди запросов для определенного региона. Операции с устройством Базовое обобщенное блочное устройство имеет open, close (освобождение), ioctl и, что самое главное, функцию request. По крайней мере функции open и close могут быть простыми счетчиками. Интерфейс ioctl() может использоваться для отладки и выполнения измерений при прохождении через различные слои программного обеспечения. Функция request, вызываемая, когда запрос помещается в очередь файловой системой, извлекает структуру запроса и обрабатывает его содержимое. В зависимости от того, является ли запрос запросом на чтение или на запись, устройство выполняет соответствующее действие. К очереди запросов нельзя получить прямой доступ, а только через вспомогательные функции. (Их можно найти в driver /block/elevator, с и include /linux/ blkdev.h.) Для сохранения совместимости с базовой моделью устройства мы можем включить возможность взаимодействия со следующим запросом в нашу функцию request: drivers/block/elevator.с 186 struct request *elv_next_request (request__queue_t *q) Эта вспомогательная функция возвращает указатель на следующую структуру запроса. Проверяя ее элементы, драйвер может получить всю информацию, необходимую для определения размера, направления и других дополнительных операций, связанных с данной очередью. Когда драйвер завершает запрос, он сообщает об этом ядру с помощью вспомогательной функции end_request (): drivers/block/ll_rw_blk.с 2599 void end_request(struct request *req, int uptodate) Устройства 2600 { 2601 if (! end_that_request_first (req, uptodate, req-> hard_.cur_sectors)) { 2602 add_disk_randomness (req-> rq_disk); 2603 blkdev_dequeue_request(req); 2604 end_that_request_last(req); 2605 } 2606 } Строка 2599 Передает очередь запроса, полученную из elev_next_request (). Строка 2601 end__that_request_first () передает соответствующее количество секторов [если секторы находятся близко, просто возвращается end_request ()]. Строка 2602 Добавляет в систему пул энтропии. Пул энтропии - это способ генерации случайных номеров в системе из функции, достаточно быстрой для вызова во время обработки прерываний. Базовая идея заключается в том, чтобы собрать байты из данных разных драйверов в системе и сгенерировать на их основе случайное число. Это обсуждается в гл. 10, «Добавление вашего кода в ядро». Еще одно объяснение находится в конце /drivers/char/random.с. Строка 2603 Удаление структуры запроса из очереди. Строка 2604 Сбор статистики и приготовление структуры к освобождению. В этой точке обобщенный драйвер обслуживает запросы до их освобождения. В соответствии с рис. 5.4 мы имеем слой обобщенного блочного устройства, создающего и поддерживающего очереди запросов. Последним слоем в системе блочного ввода-вывода является аппаратный (или специальный) драйвер устройства. Аппаратный драйвер устройства использует вспомогательные функции очереди запросов из обобщенного слоя для обслуживания запросов из зарегистрированной очереди запросов и передает уведомление, когда запрос завершен. Аппаратный драйвер устройства обладает знаниями о нижестоящем оборудовании относительно их регистров, ввода-вывода, таймера, прерываний и DMA (обсуждается в подразд. 5.2.9, «Прямой доступ к памяти (DMA)»). (Подробности реализации драйверов для ШЕ и SCSI лежат за пределами рассмотрения этой главы. Мы рассмотрим подробнее аппаратные драйверы устройств в гл. 10, а серия проектов поможет вам написать каркас для собственного драйвера.) Глава 5 • Ввод-Вывод Обзор символьных устройств В отличие от блочных устройств символьные устройства посылают поток данных. Все последовательные устройства являются символьными. Когда мы используем классический пример контроллера клавиатуры или последовательного терминала в качестве символьного устройства, становится интуитивно понятным, что мы не можем (и не хотим) получать данные от устройства не по порядку. Так мы подходим к серой области пакетной передачи данных. Сеть Ethernet на физическом уровне является последовательным устройством, но на уровне шины используется DMA для передачи в память и из памяти больших порций данных. Как разработчики драйвера устройства мы можем сделать с устройством что угодно, но на практике мы редко будем получать случайный доступ к аудиопотоку или писать поток на IDE-диск. Несмотря на то что оба примера звучат заманчиво, мы должны придерживаться двух простых правил: • все устройства ввода-вывода в Linux основаны на файлах; • все устройства ввода-вывода в Linux являются либо символьными, либо блочными. Драйвер параллельного порта в конце этой главы - символьный драйвер устройства. Символьные и блочные устройства схожи между собой интерфейсом, основанным на файловом вводе-выводе. Извне оба типа используют файловые операций, такие, как open, close, read и write. Внутри самое главное различие между символьными драйверами устройств и блочными драйверами устройств заключается в том, что символьные устройства не обладают системой блокировки для очередей запросов операций на чтение и на запись (как было сказано ранее). Зачастую для не имеющих буфера символьных устройств прерывание выполняется для каждого полученного элемента (символа). Для блочных устройств, наоборот, получается целая порция (порции) данных и затем для этой порции вызывается прерывание. Популярное:
|
Последнее изменение этой страницы: 2016-03-25; Просмотров: 725; Нарушение авторского права страницы