Морис Бах - Архитектура операционной системы UNIX
алгоритм iget
входная информация: номер индекса в файловой системе
выходная информация: заблокированный индекс
{
do {
if (индекс в индексном кеше) {
if (индекс заблокирован) {
sleep (до освобождения индекса);
continue; /* цикл с условием продолжения */
}
/* специальная обработка для точек монтирования */
if (индекс является индексом точки монтирования) {
найти запись в таблице монтирования для точки монтирования;
получить новый номер файловой системы из таблицы монтирования;
использовать номер индекса корня для просмотра;
continue; /* продолжение цикла */
}
if (индекс в списке свободных индексов) убрать из списка свободных индексов;
увеличить счетчик ссылок для индекса;
return (индекс);
}
/* индекс отсутствует в индексном кеше */
убрать новый индекс из списка свободных индексов;
сбросить номер индекса и файловой системы;
убрать индекс из старой хеш-очереди, поместить в новую;
считать индекс с диска (алгоритм bread);
инициализировать индекс (например, установив счетчик ссылок в 1);
return (индекс);
}
}
Рисунок 5.25. Модификация алгоритма получения доступа к индексу
Для второго случая пересечения точки монтирования в направлении из файловой системы, которая монтируется, в файловую систему, где выполняется монтирование, рассмотрим модификацию алгоритма namei (Рисунок 5.26). Она похожа на версию алгоритма, приведенную на Рисунке 4.11. Однако, после обнаружения в каталоге номера индекса для данной компоненты пути поиска ядро проверяет, не указывает ли номер индекса на то, что это корневой индекс файловой системы. Если это так и если текущий рабочий индекс так же является корневым, а компонента пути поиска, в свою очередь, имеет имя «..», ядро идентифицирует индекс как точку монтирования. Оно находит в таблице монтирования запись, номер устройства в которой совпадает с номером устройства для последнего из найденных индексов, получает индекс для каталога, в котором производится монтирование, и продолжает поиск компоненты с именем «..», используя только что полученный индекс в качестве рабочего. В корне файловой системы, тем не менее, корневым каталогом является «..»
алгоритм namei /* превращение имени пути поиска в индекс */
входная информация: имя пути поиска
выходная информация: заблокированный индекс
{
if (путь поиска берет начало с корня) рабочий индекс = индексу корня (алгоритм iget);
else рабочий индекс = индексу текущего каталога (алгоритм iget);
do (пока путь поиска не кончился) {
считать следующую компоненту имени пути поиска;
проверить соответствие рабочего индекса каталогу и права доступа;
if (рабочий индекс соответствует корню и компонента имени «..») continue; /* цикл с условием продолжения */
поиск компоненты:
считать каталог (рабочий индекс), повторяя алгоритмы bmap, bread и brelse;
if (компонента соответствует записи в каталоге (рабочем индексе)) {
получить номер индекса для совпавшей компоненты;
if (найденный индекс является индексом корня и рабочий индекс является индексом корня и имя компоненты «..») {
/* пересечение точки монтирования */
получить запись в таблице монтирования для рабочего индекса;
освободить рабочий индекс (алгоритм iput);
рабочий индекс = индексу точки монтирования;
заблокировать индекс точки монтирования;
увеличить значение счетчика ссылок на рабочий индекс;
перейти к поиску компоненты (для «..»);
}
освободить рабочий индекс (алгоритм iput);
рабочий индекс = индексу с новым номером (алгоритм iget);
}
else /* компонента отсутствует в каталоге */ return (нет индекса);
}
return (рабочий индекс);
}
Рисунок 5.26. Модификация алгоритма синтаксического анализа имени файла
В вышеприведенном примере (cd «../../..») предполагается, что в начале процесс имеет текущий каталог с именем «/usr/src/uts». Когда имя пути поиска подвергается анализу в алгоритме namei, начальным рабочим индексом является индекс текущего каталога. Ядро меняет текущий рабочий индекс на индекс каталога с именем «/usr/src» в результате расшифровки первой компоненты «..» в имени пути поиска. Затем ядро анализирует вторую компоненту «..» в имени пути поиска, находит корневой индекс смонтированной (перед этим) файловой системы — индекс каталога «usr» — и делает его рабочим индексом при анализе имени с помощью алгоритма namei. Наконец, оно расшифровывает третью компоненту «..» в имени пути поиска. Ядро обнаруживает, что номер индекса для «..» совпадает с номером корневого индекса, рабочим индексом является корневой индекс, а «..» является текущей компонентой имени пути поиска. Ядро находит запись в таблице монтирования, соответствующую точке монтирования «usr», освобождает текущий рабочий индекс (корень файловой системы, смонтированной в каталоге «usr») и назначает индекс точки монтирования (каталога «usr» в корневой файловой системе) в качестве нового рабочего индекса. Затем оно просматривает записи в каталоге точки монтирования «/usr» в поисках имени «..» и находит номер индекса для корня файловой системы («/»). После этого системная функция chdir завершается как обычно, вызывающий процесс не обращает внимания на тот факт, что он пересек точку монтирования.
5.14.2 Демонтирование файловой системы
Синтаксис вызова системной функции umount:
umount(special filename);
где special filename указывает демонтируемую файловую систему. При демонтировании файловой системы (Рисунок 5.27) ядро обращается к индексу демонтируемого устройства, восстанавливает номер устройства для специального файла, освобождает индекс (алгоритм iput) и находит в таблице монтирования запись с номером устройства, равным номеру устройства для специального файла. Прежде чем ядро действительно демонтирует файловую систему, оно должно удостовериться в том, что в системе не осталось используемых файлов, для этого ядро просматривает таблицу индексов в поисках всех файлов, чей номер устройства совпадает с номером демонтируемой системы. Активным файлам соответствует положительное значение счетчика ссылок и в их число входят текущий каталог процесса, файлы с разделяемым текстом, которые исполняются в текущий момент (глава 7), и открытые когда-то файлы, которые потом не были закрыты. Если какие-нибудь файлы из файловой системы активны, функция umount завершается неудачно: если бы она прошла успешно, активные файлы сделались бы недоступными.
Буферный пул все еще содержит блоки с «отложенной записью», не переписанные на диск, поэтому ядро «вымывает» их из буферного пула. Ядро удаляет записи с разделяемым текстом, которые находятся в таблице областей, но не являются действующими (подробности в главе 7), записывает на диск все недавно скорректированные суперблоки и корректирует дисковые копии всех индексов, которые требуют этого. Казалось, было бы достаточно откорректировать дисковые блоки, суперблок и индексы только для демонтируемой файловой системы, однако в целях сохранения преемственности изменений ядро выполняет аналогичные действия для всей системы в целом. Затем ядро освобождает корневой индекс монтированной файловой системы, удерживаемый с момента первого обращения к нему во время выполнения функции mount, и запускает из драйвера процедуру закрытия устройства, содержащего файловую систему. Впоследствии ядро просматривает буферы в буферном кеше и делает недействительными те из них, в которых находятся блоки демонтируемой файловой системы; в хранении информации из этих блоков в кеше больше нет необходимости. Делая буферы недействительными, ядро вставляет их в начало списка свободных буферов, в то время как блоки с актуальной информацией остаются в буферном кеше. Ядро сбрасывает в индексе системы, где производилось монтирование, флаг «точки монтирования», установленный функцией mount, и освобождает индекс. Пометив запись в таблице монтирования свободной для общего использования, функция umount завершает работу.