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

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

г-узел и зону, передаваемых из суперблока в alloc bit как начальная позиция для поиска, его удается подсократить.

Обнулить занятый бит проще, чем установить, так как не требуется выполнять поиск. Функция free bit вычисляет, какой блок битовой карты содержит сбрасываемый бит, и дальше вызывает get block, обнуляет бит и завершает операцию вызовом put block.

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

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

Наконец, перейдем к read super. Эта функция частично подобна функциям rw block и rwjnode, но выполняет только чтение. Записывать суперблок при нормальной работе системы не требуется. Считав суперблок, функция read super узнает версию файловой системы и при необходимости выполняет форматное преобразование. Таким образом, копия суперблока в памяти всегда имеет стандартный формат, даже если она прочитана с диска с другой структурой.

Работа с дескрипторами файлов

в MINIX имеются специальные подпрограммы и для работы с файловыми дескрипторами и таблицами filp (см. рис. 5.29). Эти процедуры находятся в файле filedesc.c. Когда файл создается или открывается, с ним связываются свободный дескриптор и незанятая ячейка в таблице filp. Для поиска свободных ресурсов предназначена процедура getjd. Она не помечает их как занятые, так как успешному завершению вызовов creat или open предшествует еще множество различных проверок.

Функция get filp проверяет, находится ли файловый дескриптор в заданных пределах и возвращает его указатель, взятый из filp.

Последняя процедура в файле, find filp, позволяет выяснить, что процесс пытается писать в неработающий канал (то есть канал, не открытый другим процессом на чтение). Она прямым перебором ищет в таблице filp процессы-читатели на другом конце канала. Если такой процесс найти не удалось, значит, канал разрушен и попытка записи в него обречена на провал.

Захват файлов

Функции POSIX для захвата файлов перечислены в табл. 5.8. Участок файла можно захватить на запись и чтение или только на запись при помощи системного вызова fcntl с запросом F SETLK или F SETLKW. Узнать, удерживается ли кем тот или иной участок файла, можно с помощью запроса F GETLK.



Таблица 5.8. Операции захвата записей согласно POSIX

Операция Действие

F SETLK Захватывает область на запись и чтение

F SETLKW Захватывает область на запись

F GETLK Проверяет, свободна ли область

В lock.c есть только две функции. К первой из них, 1оск ор, обращается системный вызов fcntl при выполнении каждой из операций, перечисленных в таблице. Она делает несколько тестов, гарантирующих, что участок задан корректно. Блокировка участка не должна конфликтовать с существующими, а другой захват не должен быть выполнен дважды. Для снятия захвата вызывается другая процедура из этого файла, lock revive. Она разблокирует все процессы, ранее заблокированные в ожидании данного участка. Это компромиссная стратегия, потому для точного решения, какой процесс получит управление, потребовался дополнительный код. Те процессы, время которых еще не наступило, вновь блокируются после запуска. Такая стратегия выбрана исходя из того предположения, что захват файлов - операция редкая. Если на основе MINIX будет построена многопользовательская база данных, не исключено, что потребуется изменить данный алгоритм.

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

5.7.3. Основная программа

Код главного цикла файловой системы находится в файле main.c. По своей структуре он очень схож с главным циклом менеджера памяти и задач ввода/вывода. В нем вызывается функция get work, ожидающая прибытия следующего запроса на обслуживание (если не может быть обслужен процесс, ранее приостановленный на чтении из канала или с терминала). Она же устанавливает значение глобальной переменной who, куда записывается номер ячейки в таблице процессов, принадлежащей вызывающему процессу, а также заполняет другую глобальную переменную, fs call, записывая в нее номер сделанного системного вызова.

Как только управление возвращается в главный цикл, устанавливаются три флага: fp указывает на запись вызывающего процесса в таблице процессов, super user говорит, принадлежит ли этот процесс суперпользователю, dont reply инициализируется значением FALSE. Затем начинается главное действо: на передний план выходит процедура, выполняющая системный вызов. Адрес процедуры определяется по номеру системного вызова из таблицы указателей на процедуры, calLvector.

При возврате управления в главный цикл проверяется значение флага dont reply. Если он установлен, значит, ответное сообщение не требуется (то есть процесс заблокирован по причине чтения из пустого канала). Иначе отправляется ответное сообщение. Последний оператор в цикле должен распознавать последо-



нательное чтение файла и, соответственно, пытается загрузить следующий блок до того, как в действительности придет запрос, в расчете на увеличение производительности.

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

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

Функции инициализации

Оставшуюся часть файла main.c составляют функции инициализации, отрабатывающие при запуске системы. Перед входом в главный цикл файловая система инициализирует себя, вызывая fsjnit, которая, в свою очередь, вызывает несколько других функций с целью подготовить кэш блоков, определить параметры загрузки ядра, при необходимости организовать RAM-диск, а также загрузить суперблок корневого устройства. На следующем шаге заполняется принадлежащая файловой системе часть таблицы процессов, в нее вносятся записи всех серверов и задач, до процесса init. Наконец, проверяются значения некоторых важных констант, чтобы узнать, осмысленны ли их значения, и задаче памяти отправляется сообщение, в котором передается адрес таблицы процессов в файловой системе. Впоследствии этот адрес может использоваться программой ps.

Сначала ftjnit вызывает buf pool, формирующую списки для кэширования блоков. На рис. 5.27 показано нормальное состояние кэша, где все блоки связаны друг с другом в LRU-список и хешированы. Полезно разобраться в том, откуда берется ситуация, показанная на рисунке. Сразу после инициализации процедурой buf pool все буферы находятся в списке и все они сцеплены в нулевую хеш-цепочку, как показано на рис. 5.30, а. Когда запрашивается буфер, структура приходит в состояние, показанное на рис. 5.30, б, и остается в нем, пока буфер используется. На этом рисунке мы можем видеть, что блок исключен из LRU-списка и помещен в отдельную хеш-цепочку. Обычно же блок освобождается и возвращается в LRU-список немедленно. Эта ситуация иллюстрируется рис. 5.30, в. Здесь блок, хотя и не используется более, все еще содержит информацию, и при необходимости может быть извлечен из хеш-цепочки. После того как система поработает некоторое время, почти все блоки, скорее всего, окажутся случайно распределены между различными цепочками. Список LRU при этом будет выглядеть в соответствии с рис. 5.27.

Следующая функция, get boot parameters, запрашивает у задачи системы параметры загрузки ядра. Эти значения необходимы функции load ram, выделяющей память для RAM-диска. Если при загрузке ядру передан параметр rootdev = ram

корневая файловая система блок за блоком копируется на RAM-диск (устройство ramimagedev), различные структуры файловой системы не интерпретируются.



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