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

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

жер памяти или файловая система, и, как и другие серверы, в режиме пользовательского процесса.

3.4.5. Тупики в MINIX

Как UNIX, так и MINIX относятся к тупикам одинаково, то есть полностью игнорируют проблему. В MINIX нет выделенных устройств ввода/вывода, хотя, если кто-то хочет подвесить стандартный промышленный ленточный привод DAT на персональном компьютере, это не составит особенного труда. Говоря коротко, единственное место, в котором ожидаемы взаимоблокировки, это неявные совместно используемые ресурсы, такие как ячейки таблицы процессов, ячейки таблицы г-узлов и т. д. Ни один из известных алгоритмов борьбы с тупиковыми ситуациями не подходит для подобных ресурсов, которые явно не запрашиваются.

В действительности, сказанное выше - не совсем правда. Риск того, что пользовательский процесс завернет в тупик, это одно, но в самой операционной системе есть несколько мест, где необходимо принимать значительные меры предосторожности, чтобы избежать проблем. Главное - это взаимодействие между файловой системой и менеджером памяти. Обрабатывая системный вызов exec, менеджер памяти посылает файловой системе запросы на чтение двоичных файлов (исполняемого кода профаммы). При этом, если файловая система не находится в бездействии, менеджер памяти блокируется, ожидая ее освобождения. Если файловая система в то же время попытается отправить сообшение менеджеру памяти, она также заблокируется. Итог - тупик.

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

Разфаничивать доступ к устройствам можно и без поддержки операционной системы. В качестве по-настоящему глобальной переменной, доступной всем процессам, изумительно подходит имя файла. Наличие или отсутствие определенного файла может быть легко замечено любым процессом. В MINIX, как и в UNIX-системах, существует специальный каталог, /usr/spool/locks/, где процессы могут создавать подобные файловые семафоры (lock file), чтобы обозначить занятость определенного ресурса. Файловая система MINIX также поддерживает рекомендательную блокировку файлов в стиле POSIX. Но ни один из этих механизмов не является принудительным. Все зависит от того, обращает ли программа внимание на эти механизмы или нет, и не существует способа запретить профамме использовать ресурс, уже занятый другой профаммой. Это не то же самое, что и выгрузка (preemption) ресурса, так как ничто не мешает первому процессу делать повторные попытки его использования. Другими словами, не обеспечивается взаимное исключение. В результате процесс, неправильно работающий с блокировкой, может получить неправильные результаты, но тупика не возникнет.



3.5. Блочные устройства в MINIX

в последующих разделах мы вернемся к теме главы, драйверам устройств, и подробно изучим некоторые из них. В MINIX поддерживается несколько типов блочных устройств, поэтому мы начнем с обсуждения общих аспектов. Затем мы приступим к изучению RAM-диска, жесткого диска и флоппи-дисковода. Каждое из этих устройств по-своему интересно. RAM-диск необычен тем, что это устройство обладает всеми атрибутами обычного блочного устройства, за исключением того, что при его работе никакого реального ввода/вывода не происходит. Этот диск целиком находится в оперативной памяти. Поэтому драйвер устроен просто и является хорошей отправной точкой для изучения. Жесткий диск демонстрирует, на что похож драйвер реального диска. Некоторым может показаться, что дискетный привод (накопитель на гибких дисках) проще, чем жесткий диск, но это не так. Мы не будем обсуждать драйвер гибкого диска в подробностях, но укажем на несколько имеющихся в нем сложных мест.

После раздела, посвященного драйверам блочных устройств, мы обсудим другие классы устройств в MINIX. Таймер важен тем, что он присутствует в каждой системе и полностью отличается от всех остальных устройств. Это также любопытный пример устройства, которое не является ни блочным, ни символьным. В завершение мы рассмотрим драйвер терминала, который важен для любой системы и, к тому же, является удачным примером драйвера символьного устройства.

Каждый из разделов включает в себя описание соответствующего оборудования, принципов построения программы драйвера, обзор реализации и сам код. Такая организация делает информацию полезной и для тех, кто не заинтересован в деталях.

3.5.1. Обзор драйверов блочных устройств в MINIX

Ранее мы упомянули, что основной код у большинства задач ввода/вывода имеет общую структуру. У MINIX в ядре всегда в наличии три встроенных задачи ввода/вывода: для RAM-диска, флоппи-дисковода и один из нескольких драйверов жесткого диска. Кроме того, при необходимости в ядро нетрудно добавить драйверы компакт-дисков или SCSI. Несмотря на то что все драйверы работают как отдельные процессы, они разделяют одно адресное пространство и поэтому могут использовать общий код, например различные вспомогательные подпрограммы.

Конечно, каждому драйверу необходимо выполнить некоторые действия для инициализации. Драйвер RAM-диска должен зарезервировать необходимый объем памяти, драйвер жесткого диска - определить параметры устройства и т. д. Все драйверы дисков вызываются, инициализируются и, завершив все необходимые действия, входят в бесконечный цикл. Этот цикл никогда не завершается, в нем нет команды выхода. В цикле драйвер получает сообщение, вызывается одна из функций обработки и генерируется ответное сообщение.



Основной цикл, управляющий работой каждой из задач в ядре, не просто является копией некой библиотечной функции, встроенной в код. В исполняемом коде MINIX имеется только один такой цикл. Каждый драйвер передает в главный цикл параметр, представляющий собой указатель на таблицу адресов функций, которые драйвер будет использовать для обработки запросов. Далее функции вызываются из тела цикла при помощи косвенного вызова: code = (*entry points->dev-read)(&mess):

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

Листинг 3.2. Общая подпрограмма главного цикла для задач ввода/вывода

message mess: /* Буфер сообщения */

void shared io task(struct driver table *entry points) { /* Перед входом в эту функцию каждая из задач инициализируется */ while (TRUE) { receive (ANY. &mess): caller = mess.source: switchCmess.type) { case READ:

rcode = (*entry points->dev read)(&mess): break: case WRITE:

rcode - (*entry points->dev write)(&mess): break: /* Тут находится код для выполнения других действий,

таких как OPEN. CLOSE и IOCTL */ default:

rcode - ERROR:

mess.type = TASK REPLY:

mess.status = rcode: /* Код возврата */

send(caller. &mess): /* Отправляем ответ */

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

Есть шесть различных действий, которые могут быть запрошены у каждого драйвера устройства. Им соответствуют значения, перечисленные для поля m.m type в табл. 3.4. Вот они: OPEN, CLOSE, READ, WRITE, IOCTL, SCATTEREDJO.

Большая часть этих операций, вероятно, знакома читателям, имеющим опыт профаммирования. На уровне драйвера устройства они сводятся к системным вызовам с соответствующим именем. Смысл операций READ и WRITE, например.



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 - 2018 ULTRASONEX-AMFODENT.RU.
Копирование материалов разрешено исключительно при условии цититирования.