The design of the unix operating system by Maurice J

Вид материалаРеферат
Подобный материал:
1   ...   12   13   14   15   16   17   18   19   ...   55

точку монтирования в третьей компоненте ".." имени.

Для случая пересечения точки монтирования в направлении из

файловой системы, где производится монтирование, в файловую сис-

тему, которая монтируется, рассмотрим модификацию алгоритма iget

(Рисунок 5.25), которая идентична версии алгоритма, приведенной

на Рисунке 4.3, почти во всем, за исключением того, что в данной

модификации производится проверка, является ли индекс индексом

точки монтирования. Если индекс имеет соответствующую пометку,

ядро соглашается, что это индекс точки монтирования. Оно обнару-

живает в таблице монтирования запись с указанным индексом точки

монтирования и запоминает номер устройства монтируемой файловой

системы. Затем, используя номер устройства и номер индекса корня,

общего для всех файловых систем, ядро обращается к индексу корня


-------------------------------------------------------------┐

│ алгоритм iget │

│ входная информация: номер индекса в файловой системе │

│ выходная информация: заблокированный индекс │

│ { │

│ выполнить │

│ { │

│ если (индекс в индексном кеше) │

│ { │

│ если (индекс заблокирован) │

│ { │

│ приостановиться (до освобождения индекса); │

│ продолжить; /* цикл с условием продолжения */ │

│ } │

│ /* специальная обработка для точек монтирования */ │

│ если (индекс является индексом точки монтирования) │

│ { │

│ найти запись в таблице монтирования для точки мон- │

│ тирования; │

│ получить новый номер файловой системы из таблицы │

│ монтирования; │

│ использовать номер индекса корня для просмотра; │

│ продолжить; /* продолжение цикла */ │

│ } │

│ если (индекс в списке свободных индексов) │

│ убрать из списка свободных индексов; │

│ увеличить счетчик ссылок для индекса; │

│ возвратить (индекс); │

│ } │

│ │

│ /* индекс отсутствует в индексном кеше */ │

│ убрать новый индекс из списка свободных индексов; │

│ сбросить номер индекса и файловой системы; │

│ убрать индекс из старой хеш-очереди, поместить в новую;│

│ считать индекс с диска (алгоритм bread); │

│ инициализировать индекс (например, установив счетчик │

│ ссылок в 1); │

│ возвратить (индекс); │

│ } │

│ } │

L-------------------------------------------------------------


Рисунок 5.25. Модификация алгоритма получения доступа к ин-

дексу


монтируемого устройства и возвращает при выходе из функции этот

индекс. В первом примере смены каталога ядро обращается к индексу

каталога "/usr" из файловой системы, в которой производится мон-

тирование, обнаруживает, что этот индекс имеет пометку "точка

монтирования", находит в таблице монтирования индекс корня монти-

руемой файловой системы и обращается к этому индексу.


-------------------------------------------------------------┐

│ алгоритм namei /* превращение имени пути поиска в индекс */│

│ входная информация: имя пути поиска │

│ выходная информация: заблокированный индекс │

│ { │

│ если (путь поиска берет начало с корня) │

│ рабочий индекс = индексу корня (алгоритм iget); │

│ в противном случае │

│ рабочий индекс = индексу текущего каталога │

│ (алгоритм iget); │

│ │

│ выполнить (пока путь поиска не кончился) │

│ { │

│ считать следующую компоненту имени пути поиска; │

│ проверить соответствие рабочего индекса каталогу │

│ и права доступа; │

│ если (рабочий индекс соответствует корню и компо- │

│ нента имени "..") │

│ продолжить; /* цикл с условием продолжения */│

│ поиск компоненты: │

│ считать каталог (рабочий индекс), повторяя алго- │

│ ритмы bmap, bread и brelse; │

│ если (компонента соответствует записи в каталоге │

│ (рабочем индексе)) │

│ { │

│ получить номер индекса для совпавшей компонен-│

│ ты; │

│ если (найденный индекс является индексом кор- │

│ ня и рабочий индекс является индексом корня │

│ и имя компоненты "..") │

│ { │

│ /* пересечение точки монтирования */ │

│ получить запись в таблице монтирования для │

│ рабочего индекса; │

│ освободить рабочий индекс (алгоритм iput); │

│ рабочий индекс = индексу точки монтирования;│

│ заблокировать индекс точки монтирования; │

│ увеличить значение счетчика ссылок на рабо- │

│ чий индекс; │

│ перейти к поиску компоненты (для ".."); │

│ } │

│ освободить рабочий индекс (алгоритм iput); │

│ рабочий индекс = индексу с новым номером │

│ (алгоритм iget); │

│ } │


│ в противном случае /* компонента отсутствует в │

│ каталоге */ │

│ возвратить (нет индекса); │

│ } │

│ возвратить (рабочий индекс); │

│ } │

L-------------------------------------------------------------


Рисунок 5.26. Модификация алгоритма синтаксического анализа

имени файла


Для второго случая пересечения точки монтирования в направле-

нии из файловой системы, которая монтируется, в файловую систему,

где выполняется монтирование, рассмотрим модификацию алгоритма

namei (Рисунок 5.26). Она похожа на версию алгоритма, приведенную

на Рисунке 4.11. Однако, после обнаружения в каталоге номера ин-

декса для данной компоненты пути поиска ядро проверяет, не указы-

вает ли номер индекса на то, что это корневой индекс файловой

системы. Если это так и если текущий рабочий индекс так же явля-

ется корневым, а компонента пути поиска, в свою очередь, имеет

имя "..", ядро идентифицирует индекс как точку монтирования. Оно

находит в таблице монтирования запись, номер устройства в которой

совпадает с номером устройства для последнего из найденных индек-

сов, получает индекс для каталога, в котором производится монти-

рование, и продолжает поиск компоненты с именем "..", используя

только что полученный индекс в качестве рабочего. В корне файло-

вой системы, тем не менее, корневым каталогом является "..".

В вышеприведенном примере (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), записывает на диск все недавно скорректированные су-

перблоки и корректирует дисковые копии всех индексов, которые

требуют этого. Казалось, было бы достаточно откорректировать дис-

ковые блоки, суперблок и индексы только для демонтируемой файло-

вой системы, однако в целях сохранения преемственности изменений


-------------------------------------------------------------┐

│ алгоритм umount │

│ входная информация: имя специального файла, соответствую- │

│ щего демонтируемой файловой системе │

│ выходная информация: отсутствует │

│ { │

│ если (пользователь не является суперпользователем) │

│ возвратить (ошибку); │

│ получить индекс специального файла (алгоритм namei); │

│ извлечь старший и младший номера демонтируемого устрой-│

│ ства; │

│ получить в таблице монтирования запись для демонтируе- │

│ мой системы, исходя из старшего и младшего номеров; │

│ освободить индекс специального файла (алгоритм iput); │

│ удалить из таблицы областей записи с разделяемым текс- │

│ том для файлов, принадлежащих файловой │

│ системе; /* глава 7ххх */ │

│ скорректировать суперблок, индексы, выгрузить буферы │

│ на диск; │

│ если (какие-то файлы из файловой системы все еще ис- │

│ пользуются) │

│ возвратить (ошибку); │

│ получить из таблицы монтирования корневой индекс монти-│

│ рованной файловой системы; │

│ заблокировать индекс; │

│ освободить индекс (алгоритм iput); /* iget был при │

│ монтировании */ │

│ запустить процедуру закрытия для специального устрой- │

│ ства; │


│ сделать недействительными (отменить) в пуле буферы из │

│ демонтируемой файловой системы; │

│ получить из таблицы монтирования индекс точки монтиро- │

│ вания; │

│ заблокировать индекс; │

│ очистить флаг, помечающий индекс как "точку монтирова- │

│ ния"; │

│ освободить индекс (алгоритм iput); /* iget был при │

│ монтировании */ │

│ освободить буфер, используемый под суперблок; │

│ освободить в таблице монтирования место, занятое ранее;│

│ } │

L-------------------------------------------------------------


Рисунок 5.27. Алгоритм демонтирования файловой системы


ядро выполняет аналогичные действия для всей системы в целом. За-

тем ядро освобождает корневой индекс монтированной файловой сис-

темы, удерживаемый с момента первого обращения к нему во время

выполнения функции mount, и запускает из драйвера процедуру зак-

рытия устройства, содержащего файловую систему. Впоследствии ядро

просматривает буферы в буферном кеше и делает недействительными

те из них, в которых находятся блоки демонтируемой файловой сис-

темы; в хранении информации из этих блоков в кеше больше нет не-

обходимости. Делая буферы недействительными, ядро вставляет их в

начало списка свободных буферов, в то время как блоки с актуаль-

ной информацией остаются в буферном кеше. Ядро сбрасывает в ин-

дексе системы, где производилось монтирование, флаг "точки монти-

рования", установленный функцией mount, и освобождает индекс.

Пометив запись в таблице монтирования свободной для общего ис-

пользования, функция umount завершает работу.


/





usr



-------------+-------------┐

│ │

src include

│ │

│ -----+----┐

uts sys realfile.h





sys



--------+-------┐

inode.h testfile.h


Рисунок 5.28. Файлы в дереве файловой системы, связанные с

помощью функции link


5.15 LINK


Системная функция link связывает файл с новым именем в струк-

туре каталогов файловой системы, создавая для существующего ин-

декса новую запись в каталоге. Синтаксис вызова функции link:

link(source file name, target file name);

где source file name - существующее имя файла, а target file

name - новое (дополнительное) имя, присваиваемое файлу после вы-

полнения функции link. Файловая система хранит имя пути поиска

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

к файлу по любому из этих имен. Ядро не знает, какое из имен фай-

ла является его подлинным именем, поэтому имя файла специально не

обрабатывается. Например, после выполнения набора функций:

link("/usr/src/uts/sys","/usr/include/sys");

link("/usr/include/realfile.h","/usr/src/uts/sys/testfile.h");

на один и тот же файл будут указывать три имени пути поиска:

"/usr/src/uts/sys/testfile.h", "/usr/include/sys/testfile.h" и

"/usr/include/realfile" (см. Рисунок 5.28).

Ядро позволяет суперпользователю (и только ему) связывать ка-

талоги, упрощая написание программ, требующих пересечения дерева

файловой системы. Если бы это было разрешено произвольному поль-

зователю, программам, пересекающим иерархическую структуру фай-

лов, пришлось бы заботиться о том, чтобы не попасть в бесконечный

цикл в том случае, если пользователь связал каталог с вершиной,

стоящей ниже в иерархии. Предполагается, что суперпользователи

более осторожны в указании таких связей. Возможность связывать

между собой каталоги должна была поддерживаться в ранних версиях

системы, так как эта возможность требуется для реализации команды

mkdir, которая создает новый каталог. Включение функции mkdir ус-

траняет необходимость в связывании каталогов.


-------------------------------------------------------------┐

│ алгоритм link │

│ входная информация: существующее имя файла │

│ новое имя файла │

│ выходная информация: отсутствует │

│ { │

│ получить индекс для существующего имени файла (алгоритм │

│ namei); │

│ если (у файла слишком много связей или производится │

│ связывание каталога без разрешения суперпользователя) │

│ { │

│ освободить индекс (алгоритм iput); │

│ возвратить (ошибку); │

│ } │

│ увеличить значение счетчика связей в индексе; │

│ откорректировать дисковую копию индекса; │

│ снять блокировку с индекса; │

│ получить индекс родительского каталога для включения но-│

│ вого имени файла (алгоритм namei); │

│ если (файл с новым именем уже существует или существую- │

│ щий файл и новый файл находятся в разных файловых сис- │

│ темах) │

│ { │

│ отменить корректировку, сделанную выше; │

│ возвратить (ошибку); │

│ } │

│ создать запись в родительском каталоге для файла с но- │

│ вым именем: │

│ включить в нее новое имя и номер индекса существую- │

│ щего файла; │

│ освободить индекс родительского каталога (алгоритм │

│ iput); │

│ освободить индекс существующего файла (алгоритм iput); │

│ } │

L-------------------------------------------------------------


Рисунок 5.29. Алгоритм связывания файлов


На Рисунке 5.29 показан алгоритм функции link. Сначала ядро,

используя алгоритм namei, определяет местонахождение индекса ис-

ходного файла, увеличивает значение счетчика связей в индексе,

корректирует дисковую копию индекса (для обеспечения согласован-

ности) и снимает с индекса блокировку. Затем ядро ищет файл с но-

вым именем; если он существует, функция link завершается неудачно

и ядро восстанавливает прежнее значение счетчика связей, изменен-

ное ранее. В противном случае ядро находит в родительском катало-

ге свободную запись для файла с новым именем, записывает в нее

новое имя и номер индекса исходного файла и освобождает индекс

родительского каталога, используя алгоритм iput. Поскольку файл с

новым именем ранее не существовал, освобождать еще какой-нибудь

индекс не нужно. Ядро, освобождая индекс исходного файла, делает

заключение: счетчик связей в индексе имеет значение, на 1 боль-

шее, чем то значение, которое счетчик имел перед вызовом функции,

и обращение к файлу теперь может производиться по еще одному име-

ни в файловой системе. Счетчик связей хранит количество записей

в каталогах, которые (записи) указывают на файл, и тем самым от-

личается от счетчика ссылок в индексе. Если по завершении выпол-

нения функции link к файлу нет обращений со стороны других про-

цессов, счетчик ссылок в индексе принимает значение, равное 0, а

счетчик связей - значение, большее или равное 2.

Например, выполняя функцию, вызванную как:

link("source","/dir/target");

ядро обнаруживает индекс для файла "source", увеличивает в нем

значение счетчика связей, запоминает номер индекса, скажем 74, и

снимает с индекса блокировку. Ядро также находит индекс каталога

"dir", являющегося родительским каталогом для файла "target",

ищет свободное место в каталоге "dir" и записывает в него имя

файла "target" и номер индекса 74. По окончании этих действий оно

освобождает индекс файла "source" по алгоритму iput. Если значе-

ние счетчика связей файла "source" раньше было равно 1, то теперь

оно равно 2.

Стоит упомянуть о двух тупиковых ситуациях, явившихся причи-

ной того, что процесс снимает с индекса исходного файла блокиров-

ку после увеличения значения счетчика связей. Если бы ядро не

снимало с индекса блокировку, два процесса, выполняющие одновре-

менно следующие функции:

процесс A: link("a/b/c/d","e/f/g");

процесс B: link("e/f","a/b/c/d/ee");

зашли бы в тупик (взаимная блокировка). Предположим, что процесс

A обнаружил индекс файла "a/b/c/d" в тот самый момент, когда про-

цесс B обнаружил индекс файла "e/f". Фраза "в тот же самый мо-

мент" означает, что системой достигнуто состояние, при котором

каждый процесс получил искомый индекс. (Рисунок 5.30 иллюстрирует

стадии выполнения процессов.) Когда же теперь процесс A попытает-

ся получить индекс файла "e/f", он приостановит свое выполнение

до тех пор, пока индекс файла "f" не освободится. В то же время

процесс B пытается получить индекс каталога "a/b/c/d" и приоста-

навливается в ожидании освобождения индекса файла "d". Процесс A

будет удерживать заблокированным индекс, нужный процессу B, а