Главная страница  Межпроцессное взаимодействие (состязание) 

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 [ 67 ] 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187

вполне понятен. Эти операции передают блок данных из памяти вызвавшего процесса в устройство или наоборот. Обычно операция WRITE не передает управление в вызвавший процесс до тех пор, пока передача данных не завершена. Но операционная система может буферизовать передаваемые данные, и вызов может завершиться сразу, а данные будут переданы позже. Это никак не сказывается на сделавшей вызов профамме, сразу после него она вправе использовать буфер, так как операционная система уже скопировала нужные ей данные. Операции OPEN и CLOSE для устройства означают то же, что и системные вызовы open и close для файлов. Операция OPEN проверяет, что устройство доступно, и возвращает код ошибки, если это не так. Операция CLOSE должна гарантировать, что все буферизованные данные переданы до полного завершения обмена информацией с устройством.

Возможно, назначение операции I0CTL не столь очевидно. У многих устройств ввода/вывода есть парамефы работы, значение которых иногда нужно определять и, предположительно, менять. Выполнение этих действий обеспечивает операция I0CTL. Хороший пример использования I0CTL - управление скоростью последовательного интерфейса или параметров конфоля четности. Для блочных устройств I0CTL применяется реже. В частности, в MINIX при помощи I0CTL можно управлять тем, как диск разбит на разделы (хотя это делается просто при помощи операций чтения и записи).

Операция SCATTEREDJO, без сомнений, известна менее всех других. Дело в том, что трудно достигнуть приемлемой производительности блочного устройства, если запрашивать данные последовательно, отдельными порциями. Исключение составляют лишь исключительно бысфые устройства, такие как RAM-диск. Запрос SCATTEREDJO позволяет системе делать запросы на чтение или запись нескольких блоков. В случае операции READ система пытается предугадать, какие блоки может запросить процесс, и считывает их заранее. В таком запросе не все затребованные данные обязательны. Каждый из запросов блока данных, передаваемых драйверу устройства, можно модифицировать, установив бит, сообщающий, что запрос необязателен. В результате файловая система может создать запрос, который в переводе на человеческий язык звучал бы так: Было бы неплохо прочитать все эти данные, но все они вовсе не нужны мне прямо сейчас . Далее устройство само решает, что лучше делать. Например, гибкий диск может прочитать все данные в пределах одной дорожки, говоря тем самым: Эти данные я для тебя прочитаю, а остальные попроси потом, мне слишком долго переходить на другую дорожку .

Когда необходимо записать данные, не возникает вопроса, обязательно ли записывать каждый отдельный блок. Но система вправе буферизовать несколько запросов на запись в надежде на то, что сразу их удастся обработать быстрее, чем по отдельности. При запросе SCATTERED I0, будь то для чтения или для записи, список запрошенных блоков сортируется, что позволяет считывать данные более эффективно, чем если бы они считывались в порядке поступления. Кроме того, передача нескольких блоков за один раз уменьшает количество сообщений, пересылаемых внутри MINIX.



3.5.2. Общие программы драйверов блочных устройств

Определения, присущие всем драйверам блочных устройств, находятся в файле driver, h. Самая главная часть этого файла - структура driver, посредством которой в главный цикл передается список адресов функций, выполняющих запросы. Дополнительно здесь определяется структура device, в которой хранится наиболее важная информация о разделах, базовый адрес и размер в байтах. Формат этой структуры был выбран так, чтобы основанные на оперативной памяти устройства могли использовать данные без преобразования, обеспечивая высокую скорость отклика. У реальных дисков существует такое большое количество разнообразных, влияющих на время отклика факторов, что преобразование байтовых адресов в секторы не ифает принципиальной роли.

Главный цикл и общие функции всех драйверов блочных устройств находятся в файле driver.c. Выполнив необходимые действия по инициализации, каждый драйвер вызывает процедуру driver task, передавая в качестве аргумента вызова заполненную структуру driver. Далее определяется адрес буфера DMA-устройства и управление переходит в главный цикл, откуда обратной дороги, то есть команды возврата, нет.

Единственный процесс, который должен отправлять сообщения драйверам, - это файловая система. Координация производится оператором switch в теле главного цикла. Оператор switch игнорирует остающиеся аппаратные прерывания, а сообщения от других отправителей вызывают вывод на экран предупреждения. На первый взгляд это кажется достаточно безвредным, но процессы, которые отправили сообщение неверному адресату, вероятно, навсегда останутся в состоянии блокировки, ожидая ответа. Второй оператор switch обрабатывает запросы. Первые три типа сообщений, DEV OPEN, DEV CLOSE и DEVJOCTL, приводят к косвенному вызову одной из функций, адреса которых передаются в структуре driver. Сообщения DEV READ, DEV WRITE и SCATTERED IO передают управление в do rdwt или do vrdwt. Заметьте, что в любые вызовы, косвенные или прямые, передается структура driver, поэтому все вызываемые подпрограммы могут к ней обращаться.

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

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



Причина, по которой эта инициализация вообще нужна, кроется в странностях работы оригинальных IBM PC, для которых было необходимо, чтобы буфер не пересекал границу в 64 Кбайт. Другими словами, если размер буфера 1 Кбайт, он может начинаться по адресу 64510, но не по адресу 64514, так как в последнем случае будет превышена граница, находящаяся по адресу 65536.

Это неприятное требование проистекает из того, что в IBM PC применялся старый чип контроллера прерываний Intel 8237А с 16-битным счетчиком. Так как для DMA используются абсолютные, а не относительные сегментные адреса, требовался счетчик большей емкости. На старых машинах, способных адресовать только 1 Мбайт памяти, младшие 16 бит адреса DMA зафужались в 8237А, а оставшиеся старшие 4 бита помещались в 4-разрядный регистр-переключатель. У более современных машин применяется 8-разрядный переключатель, что позволяет адресовать 16 Мбайт. Но когда адрес в 8237А переходит от OxFFFF к 0x0000, не формируется признак переноса, и адрес DMA внезапно прыгает вниз на 64 Кбайт.

Переносимая программа на языке С не может оперировать абсолютными адресами, поэтому не в силах предотвратить недопустимого положения буфера. Эта проблема решается так: выделяется буфер вдвое большего размера, чем нужно, а специальный указатель tmp buf содержит адрес реально используемого буфера. Функция 1nit buffer сначала пробует установить этот указатель так, чтобы он ссылался на начало выделенного буфера, затем проводит проверку, допустимо ли такое положение. Если нет, то значение указателя tmp buf увеличивается на количество реально затребованных байтов. Таким образом, всегда остается неиспользованный блок памяти, но граница 64 Кбайт никогда не попадает внутрь буфера.

У новых компьютеров семейства IBM PC более совершенные контроллеры DMA, поэтому код мог бы быть упрощен, а количество запрашиваемой памяти - уменьшено. Но нет гарантии, что любая машина будет нечувствительна к данной проблеме. Тем не менее, если вы рассчитываете на это, вы должны знать, как ошибка проявит себя, если она все-таки есть. Если требуется буфер объемом 1 Кбайт, то с вероятностью 1 /64 выделенный буфер будет непригоден для компьютера со старым контроллером DMA. Каждый раз, когда код ядра меняется так, что изменяется размер его исполняемых файлов, ошибка может проявить себя с той же вероятностью. Ошибка может не проявлять себя, поэтому, когда в следующем месяце или году она возникнет, ее свяжут с последними изменениями в коде, а не с реальной причиной. Подобные особенности аппаратного обеспечения опасны затратой недель на поиск ошибок, особенно если в технической документации не говорится о них ни слова (как в этом случае).

Следующая функция в dirver.c - это do rdwt. Она, в свою очередь, косвенным вызовом запускает одну из трех функций dr prepare, dr schedule или dr finish, опираясь на переданные адреса из структуры driver. В дальнейшем мы будем использовать запись вида *function pointer, чтобы обозначить функцию, вызываемую через указатель.

Далее проверяется, что количество переданных байтов больше нуля, после чего do rdwt вызывает *dr prepare. Этот вызов не должен провалиться , так как



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 [ 67 ] 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187

© 2000 - 2024 ULTRASONEX-AMFODENT.RU.
Копирование материалов разрешено исключительно при условии цититирования.