е считывания каждого блока ядро уменьшает размер канала в соответствии с коли- чеством считанных данных и устанавливает значение смещения в пространстве процесса так, чтобы при достижении конца канала оно указывало на его начало. Когда выполнение системной функции read завершается, ядро возобновляет вы- полнение всех приостановленных процессов записи и запоминает текущее значе- ние указателя чтения в индексе (а не в записи таблицы файлов). Если процесс пытается считать больше информации, чем фактически есть в канале, функция read завершится успешно, возвратив все данные, находящиеся в данный момент в канале, пусть даже не полностью выполнив запрос пользовате- ля. Если канал пуст, процесс обычно приостанавливается до тех пор, пока ка- кой-нибудь другой процесс не запишет данные в канал, после чего все приоста- новленные процессы, ожидающие ввода данных, возобновят свое выполнение и начнут конкурировать за чтение из канала. Если, однако, процесс открывает поименованный канал с параметром "no delay" (без задержки), функция read возвратит управление немедленно, если в канале отсутствуют данные. Операции чтения и записи в канал имеют ту же семантику, что и аналогичные операции для терминальных устройств (глава 10), она позволяет процессам игнорировать тип тех файлов, с которыми эти программы имеют дело. Если процесс ведет запись в канал и в канале нет места для всех данных, ядро помечает индекс и приостанавливает выполнение процесса до тех пор, пока канал не начнет очищаться от данных. Когда впоследствии другой процесс будет считывать данные из канала, ядро заметит существование процессов, приоста- новленных в ожидании очистки канала, и возобновит их выполнение подобно то- му, как это было объяснено выше. Исключением из этого утверждения является 107 ситуация, когда процесс записывает в канал данные, объем которых превышает емкость канала (то есть, объем данных, которые могут храниться в блоках пря- мой адресации); в этом случае ядро записывает в канал столько данных, сколь- ко он может вместить в себя, и приостанавливает процесс до тех пор, пока не освободится дополнительное место. Таким образом, возможно положение, при ко- тором записываемые данные не будут занимать непрерывное место в канале, если другие процессы ведут запись в канал в то время, на которое первый процесс прервал свою работу. Анализируя реализацию каналов, можно заметить, что интерфейс процессов согласуется с интерфейсом обычных файлов, но его воплощение отличается, так как ядро запоминает смещения для чтения и записи в индексе вместо того, что- бы делать это в таблице файлов. Ядро вынуждено хранить значения смещений для поименованных каналов в индексе для того, чтобы процессы могли совместно ис- пользовать эти значения: они не могли бы совместно использовать значения, хранящиеся в таблице файлов, так как процесс получает новую запись в таблице файлов по каждому вызову функции open. Тем не менее, совместное использова- ние смещений чтения и записи в индексе наблюдалось и до реализации поимено- ванных каналов. Процессы, обращающиеся к непоименованным каналам, разделяют доступ к каналу через общие точки входа в таблицу файлов, поэтому они могли бы по умолчанию хранить смещения записи и чтения в таблице файлов, как это принято для обычных файлов. Это не было сделано, так как процедуры низкого уровня, работающие в ядре, больше не имеют доступа к записям в таблице фай- лов: программа упростилась за счет того, что процессы совместно используют значения смещений, хранящиеся в индексе. .te1 5.12.4 Закрытие каналов При закрытии канала процесс выполняет ту же самую процедуру, что и при закрытии обычного файла, за исключением того, что ядро, прежде чем освобо- дить индекс канала, выполняет специальную обработку. Оно уменьшает количест- во процессов чтения из канала или записи в канал в зависимости от типа фай- лового дескриптора. Если значение счетчика числа записывающих в канал про- цессов становится равным 0 и имеются процессы, приостановленные в ожидании чтения данных из канала, ядро возобновляет выполнение последних и они завер- шают свои операции чтения без возврата каких-либо данных. Если становится равным 0 значение счетчика числа считывающих из канала процессов и имеются процессы, приостановленные в ожидании возможности записи данных в канал, яд- ро возобновляет выполнение последних и посылает им сигнал (глава 7) об ошиб- ке. В обоих случаях не имеет смысла продолжать держать процессы приостанов- ленными, если нет надежды на то, что состояние канала когда-нибудь изменит- ся. Например, если процесс ожидает возможности производить чтение из непои- менованного канала и в системе больше нет процессов, записывающих в этот ка- нал, значит, записывающий процесс никогда не появится. Несмотря на то, что если канал поименованный, в принципе возможно появление нового считывающего или записывающего процесса, ядро трактует эту ситуацию точно так же, как и для непоименованных каналов. Если к каналу не обращается ни один записываю- щий или считывающий процесс, ядро освобождает все информационные блоки кана- ла и переустанавливает индекс таким образом, чтобы он указывал на то, что канал пуст. Когда ядро освобождает индекс обычного канала, оно освобождает для переназначения и дисковую копию этого индекса. 5.12.5 Примеры Программа на Рисунке 5.18 иллюстрирует искусственное использование кана- лов. Процесс создает канал и входит в бесконечный цикл, записывая в канал 108 +---------------------------------+ | char string[] = "hello"; | | main() | | { | | char buf[1024]; | | char *cp1,*cp2; | | int fds[2]; | | | | cp1 = string; | | cp2 = buf; | | while(*cp1) | | *cp2++ = *cp1++; | | pipe(fds); | | for (;;) | | { | | write(fds[1],buf,6); | | read(fds[0],buf,6); | | } | | } | +---------------------------------+ Рисунок 5.18. Чтение из канала и запись в канал строку символов "hello" и считывая ее из канала. Ядру не нужно ни знать о том, что процесс, ведущий запись в канал, является и процессом, считывающим из канала, ни проявлять по этому поводу какое-либо беспокойство. Процесс, выполняющий программу, которая приведена на Рисунке 5.19, соз- дает поименованный канал с именем "fifo". Если этот процесс запущен с указа- нием второго (формального) аргумента, он пос- +------------------------------------------------------------+ | #include | | char string[] = "hello"; | | main(argc,argv) | | int argc; | | char *argv[]; | | { | | int fd; | | char buf[256]; | | | | /* создание поименованного канала с разрешением чтения и | | записи для всех пользователей */ | | mknod("fifo",010777,0); | | if(argc == 2) | | fd = open("fifo",O_WRONLY); | | else | | fd = open("fifo",O_RDONLY); | | for (;;) | | if(argc == 2) | | write(fd,string,6); | | else | | read(fd,buf,6); | | } | +------------------------------------------------------------+ Рисунок 5.19. Чтение и запись в поименованный канал 109 тоянно записывает в канал строку символов "hello"; будучи запущен без второ- го аргумента, он ведет чтение из поименованного канала. Два процесса запус- каются по одной и той же программе, тайно договорившись взаимодействовать между собой через поименованный канал "fifo", но им нет необходимости быть родственными процессами. Другие пользователи могут выполнять программу и участвовать в диалоге (или мешать ему). 5.13 DUР Системная функция dup копирует дескриптор файла в первое свободное место в таблице пользовательских дескрипторов файла, возвращая новый дескриптор пользователю. Она действует для всех типов файла. Синтаксис вызова функции: newfd = dup(fd); где fd - дескриптор файла, копируемый функцией, а newfd - новый дескриптор, ссылающийся на файл. Поскольку функция dup дублирует дескриптор файла, она увеличивает значение счетчика в соответствующей записи таблицы файлов - за- таблица пользова- тельских дескрип- торов файла таблица файлов таблица индексов +---------+ +------------+ +--------------+ 0| ----+----+ | | | - | +---------+ | | | | - | 1| ----+---++-->| | | - | +---------+ | +------------+ | - | 2| ----+--++--->| - | | - | +---------+ +---->| - | | - | 3| ----+----+ | - | | - | +---------+ | | - | +--------------+ 4| ----+---+| | - | +---->| счет- | +---------+ || | - | | | чик (/etc/ | 5| ----+--+|| +------------+ | +-->| 2 passwd)| +---------+ ||| | счет- | | | +--------------+ 6| ----++ ||+-->| чик +--+ | | - | +---------++-||--->| 2 | | | - | 7| | || +------------+ | | - | +---------+ || | - | | | - | | - | || +------------+ | | - | +---------+ || | счетчик | | | - | |+--->| 1 +----|+ | - | | +------------+ || | - | | | - | || +--------------+ | | - | || | счет- | | | - | |+->| чик (local)| | | - | | | 1 | | | - | | +--------------+ | +------------+ | | - | | | счетчик | | | - | +---->| 1 +----+ | - | +------------+ | - | | - | +--------------+ | - | +------------+ Рисунок 5.20. Структуры данных после выполнения функции dup 110 писи, на которую указывают связанные с ней точки входа в таблице файловых дескрипторов, которых теперь стало на одну больше. Например, обзор структур данных, изображенных на Рисунке 5.20, показывает, что процесс вызывает сле- дующую последовательность функций: он открывает (open) файл с именем "/etc/passwd" (файловый дескриптор 3), затем открывает файл с именем "local" (файловый дескриптор 4), снова файл с именем "/etc/passwd" (файловый деск- риптор 5) и, наконец, дублирует (dup) файловый дескриптор 3, возвращая деск- риптор 6. Возможно, dup - функция, не отличающаяся изяществом, поскольку она пред- полагает, что пользователь знает о том, что система возвратит свободную точ- ку входа в таблице пользовательских дескрипторов, имеющую наименьший номер. Однако, она служит важной задаче конструирования сложных программ из более простых конструкционных блоков, что, в частности, имеет место при создании конвейеров, составленных из командных процессоров. Рассмотрим программу, приведенную на Рисунке 5.21. В переменной i хра- нится дескриптор файла, возвращаемый в результате открытия файла "/etc/passwd", а в переменной j - дескриптор файла, возвращаемый системой в результате дублирования дескриптора i с помощью функции dup. В адресном пространстве процесса оба пользовательских дескриптора, представленные пере- менными i и j, ссылаются на одну и ту же запись в таблице файлов и поэтому используют одно и то же значение смещения внутри файла. Таким образом, пер- вые два вызова процессом функции read реализуют последовательное считывание данных, и в буферах buf1 и buf2 будут располагаться разные данные. Совсем другой результат получается, когда процесс +--------------------------------------------------------+ | #include | | main() | | { | | int i,j; | | char buf1[512],buf2[512]; | | | | i = open("/etc/passwd",O_RDONLY); | | j = dup(i); | | read(i,buf1,sizeof(buf1)); | | read(j,buf2,sizeof(buf2)); | | close(i); | | read(j,buf2,sizeof(buf2)); | | } | +--------------------------------------------------------+ Рисунок 5.21. Программа на языке Си, иллюстрирующая использо- вание функции dup открывает один и тот же файл дважды и читает дважды одни и те же данные (раздел 5.2). Процесс может освободить с помощью функции close любой из фай- ловых дескрипторов по своему желанию, и ввод-вывод получит нормальное про- должение по другому дескриптору, как показано на примере. В частности, про- цесс может "закрыть" дескриптор файла стандартного вывода (файловый дескрип- тор 1), снять с него копию, имеющую то же значение, и затем рассматривать новый файл в качестве файла стандартного вывода. В главе 7 будет представлен более реалистический пример использования функций pipe и dup при описании особенностей реализации командного процессора. 111 5.14 МОНТИРОВАНИЕ И ДЕМОНТИРОВАНИЕ ФАЙЛОВЫХ СИСТЕМ Физический диск состоит из нескольких логических разделов, на которые он разбит дисковым драйвером, причем каждому разделу соответствует файл устрой- ства, имеющий определенное имя. Процессы обращаются к данным раздела, откры- вая соответствующий файл устройства и затем ведя запись и чтение из этого "файла", представляя его себе в виде последовательности дисковых блоков. Это взаимодействие во всех деталях рассматривается в главе 10. Раздел диска мо- жет содержать логическую файловую систему, состоящую из блока начальной заг- рузки, суперблока, списка индексов и информационных блоков (см. главу 2). Системная функция mount (монтировать) связывает файловую систему из указан- ного раздела на диске с существующей иерархией файловых систем, а функция umount (демонтировать) выключает файловую систему из иерархии. Функция mount, таким образом, дает пользователям возможность обращаться к данным в дисковом разделе как к файловой системе, а не как к последовательности дис- ковых блоков. Синтаксис вызова функции mount: mount(special pathname,directory pathname,options); где special pathname - имя специального файла устройства, соответствующего дисковому разделу с монтируемой файловой системой, directory pathname - ка- талог в существующей иерархии, где будет монтироваться файловая система (другими словами, точка или место монтирования), а options указывает, следу- ет ли монтировать файловую систему "только для чтения" (при этом не будут выполняться + - - - - - - - - - - - - - - - - - - - - - - - - + / | | | +----------------+--+-------------+ Корневая | | | | | файловая bin etc usr система | | | - | +-----+-----+ +----+----+ - | | | | | | - | cc date sh getty passwd - + - - - - - - - - - - - - - - - - - - - - - - - - + - + - - - - - - - - - - - - - - - - - - - - - - - - + / | | | Файловая +----------------+--+-------------+ система из | | | | | раздела с bin include src именем | | | | | /dev/dsk1 +-----+-----+ | | | | | | | | | awk banner yacc stdio.h uts + - - - - - - - - - - - - - - - - - - - - - - - - + Рисунок 5.22. Дерево файловых систем до и после выполнения функции mount такие функции, как write и creat, которые производят запись в файловую сис- тему). Например, если процесс вызывает функцию mount следующим образом: mount("/dev/dsk1","/usr",0); ядро присоединяет файловую систему, находящуюся в дисковом разделе с именем "/dev/dsk1", к каталогу "/usr" в существующем дереве файловых систем (см. 112 Рисунок 5.22). Файл "/dev/dsk1" является блочным специальным файлом, т.е. он носит имя устройства блочного типа, обычно имя раздела на диске. Ядро пред- полагает, что раздел на диске с указанным именем содержит файловую систему с суперблоком, списком индексов и корневым индексом. После выполнения функции mount к корню смонтированной файловой системы можно обращаться по имени "/usr". Процессы могут обращаться к файлам в монтированной файловой системе и игнорировать тот факт, что система может отсоединяться. Только системная функция link контролирует файловую систему, так как в версии V не разрешают- ся связи между файлами, принадлежащими разным файловым системам (см. раздел 5.15). Ядро поддерживает таблицу монтирования с записями о каждой монтированной файловой системе. В каждой записи таблицы монтирования содержатся: * номер устройства, идентифицирующий монтированную файловую систему (упо- мянутый выше логический номер файловой системы); * указатель на буфер, где находится суперблок файловой системы; * указатель на корневой индекс монтированной файловой системы ("/" для файловой системы с именем "/dev/dsk1" на Рисунке 5.22); * указатель на индекс каталога, ставшего точкой монтирования (на Рисунке 5.22 это каталог "usr", принадлежащий корневой файловой системе). Связь индекса точки монтирования с корневым индексом монтированной фай- ловой системы, возникшая в результате выполнения системной функции mount, дает ядру возможность легко двигаться по иерархии файловых систем без полу- чения от пользователей дополнительных сведений. +------------------------------------------------------------+ | алгоритм mount | | входная информация: имя блочного специального файла | | имя каталога точки монтирования | | опции ("только для чтения") | | выходная информация: отсутствует | | { | | если (пользователь не является суперпользователем) | | возвратить (ошибку); | | получить индекс для блочного специального файла (алго- | | ритм namei); | | проверить допустимость значений параметров; | | получить индекс для имени каталога, где производится | | монтирование (алгоритм namei); | | если (индекс не является индексом каталога или счетчик | | ссылок имеет значение > 1) | | { | | освободить индексы (алгоритм iput); | | возвратить (ошибку); | | } | | найти свободное место в таблице монтирования; | | запустить процедуру открытия блочного устройства для | | данного драйвера; | | получить свободный буфер из буферного кеша; | | считать суперблок в свободный буфер; | | проинициализировать поля суперблока; | | получить корневой индекс монтируемой системы (алгоритм | | iget), сохранить его в таблице монтирования; | | сделать пометку в индексе каталога о том, что каталог | | является точкой монтирования; | | освободить индекс специального файла (алгоритм iput); | | снять блокировку с индекса каталога точки монтирования;| | } | +------------------------------------------------------------+ Рисунок 5.23. Алгоритм монтирования файловой системы 113 На Рисунке 5.23 показан алгоритм монтирования файловой системы. Ядро позволяет монтировать и демонтировать файловые системы только тем процессам, владельцем которых является суперпользователь. Предоставление возможности выполнять функции mount и umount всем пользователям привело бы к внесению с их стороны хаоса в работу файловой системы, как умышленному, так и явившему- ся результатом неосторожности. Суперпользователи могут разрушить систему только случайно. Ядро находит индекс специального файла, представляющего файловую систе- му, подлежащую монтированию, извлекает старший и младший номера, которые идентифицируют соответствующий дисковый раздел, и выбирает индекс каталога, в котором файловая система будет смонтирована. Счетчик ссылок в индексе ка- талога должен иметь значение, не превышающее 1 (и меньше 1 он не должен быть - почему?), в связи с наличием потенциально опасных побочных эффектов (см. упражнение 5.27). Затем ядро назначает свободное место в таблице монтирова- ния, помечает его для использования и присваивает значение полю номера уст- ройства в таблице. Вышеуказанные назначения производятся немедленно, пос- кольку вызывающий процесс может приостановиться, следуя процедуре открытия устройства или считывая суперблок файловой системы, а другой процесс тем временем попытался бы смонтировать файловую систему. Пометив для использова- ния запись в таблице монтирования, ядро не допускает использования в двух вызовах функции mount одной и той же записи таблицы. Запоминая номер устрой- ства с монтируемой системой, ядро может воспрепятствовать повторному монти- рованию одной и той же системы другими процессами, которое, будь оно допуще- но, могло бы привести к непредсказуемым последствиям (см. упражнение 5.26). Ядро вызывает процедуру открытия для блочного устройства, содержащего файловую систему, точно так же, как оно делает это при непосредственном отк- рытии блочного устройства (глава 10). Процедура открытия устройства обычно проверяет существование такого устройства, иногда производя инициализацию структур данных драйвера и посылая команды инициализации аппаратуре. Затем ядро выделяет из буферного пула свободный буфер (вариант алгоритма getblk) для хранения суперблока монтируемой файловой системы и считывает суперблок, используя один из вариантов алгоритма read. Ядро сохраняет указатель на ин- декс каталога, в котором монтируется система, давая возможность маршрутам поиска файловых имен, содержащих имя "..", пересекать точку монтирования, как мы увидим дальше. Оно находит корневой индекс монтируемой файловой сис- темы и запоминает указатель на индекс в таблице монтирования. С точки зрения пользователя, место (точка) монтирования и корень файловой системы логически эквивалентны, и ядро упрочивает эту эквивалентность благодаря их сосущество- ванию в одной записи таблицы монтирования. Процессы больше не могут обра- щаться к индексу каталога - точки монтирования. Ядро инициализирует поля в суперблоке файловой системы, очищая поля для списка свободных блоков и списка свободных индексов и устанавливая число свободных индексов в суперблоке равным 0. Целью инициализации (задания на- чальных значений полей) является сведение к минимуму опасности разрушить файловую систему, если монтирование осуществляется после аварийного заверше- ния работы системы. Если ядро заставить думать, что в суперблоке отсутствуют свободные индексы, то это приведет к запуску алгоритма ialloc, ведущего по- иск на диске свободных индексов. К сожалению, если список свободных дисковых блоков испорчен, ядро не исправляет этот список изнутри (см. раздел 5.17 о сопровождении файловой системы). Если пользователь монтирует файловую систе- му только для чтения, запрещая проведение всех операций записи в системе, ядро устанавливает в суперблоке соответствующий флаг. Наконец, ядро помечает индекс каталога как "точку монтирования", чтобы другие процессы позднее мог- ли ссылаться на нее. На Рисунке 5.24 представлен вид различных структур дан- ных по завершении выполнения функции mount. 114 5.14.1 Пересечение точек монтирования в маршрутах поиска имен файлов Давайте повторно рассмотрим поведение алгоритмов namei и iget в случаях, когда маршрут поиска файлов проходит через точку монтирования. Точку монти- рования можно пересечь двумя способами: из файловой системы, где производит- ся монтирование, в файловую систему, которая монтируется (в направлении от глобального корня к листу), и в обратном направлении. Эти способы иллюстри- рует следующая последовательность команд shell'а. Таблица индексов Таблица монтирования +------------------+ +--------------------+ +------------------+ | | | Индекс каталога, + - - + | | | где производится | | | | монтирование | | | | +-------+ | Помечен как "точ-|<---+ | |+->| Буфер | | ка монтирования" | || | || +-------+ | Счетчик ссылок =1| | | || +------------------+ |+ >+--------------------+| | | | | Суперблок ---++ +------------------+ +---+ Индекс точки монти-| | Индекс устройства| | рования | | Не используется | +---+- Корневой индекс | | Счетчик ссылок =0| | +--------------------+ +------------------+ | | | +------------------+<---+ | | | Индекс корня мон-| | | | тируемой файловой| | | | системы | | | | Счетчик ссылок =1| +--------------------+ +------------------+ +------------------+ Рисунок 5.24. Структуры данных после монтирования mount /dev/dsk1 /usr cd /usr/src/uts cd ../../.. По команде mount после выполнения некоторых логических проверок запуска- ется системная функция mount, которая монтирует файловую систему в дисковом разделе с именем "/dev/dsk1" под управлением каталога "/usr". Первая из ко- манд cd (сменить каталог) побуждает командный процессор shell вызвать сис- темную функцию chdir, выполняя которую, ядро анализирует имя пути поиска, пересекающего точку монтирования в "/usr". Вторая из команд cd приводит к тому, что ядро анализирует имя пути поиска и пересекает точку монтирования в третьей компоненте ".." имени. Для случая пересечения точки монтирования в направлении из файловой сис- темы, где производится монтирование, в файловую систему, которая монтирует- ся, рассмотрим модификацию алгоритма iget (Рисунок 5.25), которая идентична версии алгоритма, приведенной на Рисунке 4.3, почти во всем, за исключением того, что в данной модификации производится проверка, является ли индекс ин- дексом точки монтирования. Если индекс имеет соответствующую пометку, ядро соглашается, что это индекс точки монтирования. Оно обнаруживает в таблице монтирования запись с указанным индексом точки монтирования и запоминает но- мер устройства монтируемой файловой системы. Затем, используя номер устройс- тва и номер индекса корня, общего для всех файловых систем, ядро обращается к индексу корня 115 +------------------------------------------------------------+ | алгоритм iget | | входная информация: номер индекса в файловой системе | | выходная информация: заблокированный индекс | | { | | выполнить | | { | | если (индекс в индексном кеше) | | { | | если (индекс заблокирован) | | { | | приостановиться (до освобождения индекса); | | продолжить; /* цикл с условием продолжения */ | | } | | /* специальная обработка для точек монтирования */ | | если (индекс является индексом точки монтирования) | | { | | найти запись в таблице монтирования для точки мон- | | тирования; | | получить новый номер файловой системы из таблицы | | монтирования; | | использовать номер индекса корня для просмотра; | | продолжить; /* продолжение цикла */ | | } | | если (индекс в списке свободных индексов) | | убрать из списка свободных индексов; | | увеличить счетчик ссылок для индекса; | | возвратить (индекс); | | } | | | | /* индекс отсутствует в индексном кеше */ | | убрать новый индекс из списка свободных индексов; | | сбросить номер индекса и файловой системы; | | убрать индекс из старой хеш-очереди, поместить в новую;| | считать индекс с диска (алгоритм bread); | | инициализировать индекс (например, установив счетчик | | ссылок в 1); | | возвратить (индекс); | | } | | } | +------------------------------------------------------------+ Рисунок 5.25. Модификация алгоритма получения доступа к ин- дексу монтируемого устройства и возвращает при выходе из функции этот индекс. В первом примере смены каталога ядро обращается к индексу каталога "/usr" из файловой системы, в которой производится монтирование, обнаруживает, что этот индекс имеет пометку "точка монтирования", находит в таблице монтирова- ния индекс корня монтируемой файловой системы и обращается к этому индексу. Для второго случая пересечения точки монтирования в направлении из фай- ловой системы, которая монтируется, в файловую систему, где выполняется мон- тирование, рассмотрим модификацию алгоритма namei (Рисунок 5.26). Она похожа на версию алгоритма, приведенную на Рисунке 4.11. Однако, после обнаружения в каталоге номера индекса для данной компоненты пути поиска ядро проверяет, не указывает ли номер индекса на то, что это корневой индекс файловой систе- мы. Если это так и если текущий рабочий индекс так же является корневым, а 116 +------------------------------------------------------------+ | алгоритм namei /* превращение имени пути поиска в индекс */| | входная информация: имя пути поиска | | выходная информация: заблокированный индекс | | { | | если (путь поиска берет начало с корня) | | рабочий индекс = индексу корня (алгоритм iget); | | в противном случае | | рабочий индекс = индексу текущего каталога | | (алгоритм iget); | | | | выполнить (пока путь поиска не кончился) | | { | | считать следующую компоненту имени пути поиска; | | проверить соответствие рабочего индекса каталогу | | и права доступа; | | если (рабочий индекс соответствует корню и компо- | | нента имени "..") | | продолжить; /* цикл с условием продолжения */| | поиск компоненты: | | считать каталог (рабочий индекс), повторяя алго- | | ритмы bmap, bread и brelse; | | если (компонента соответствует записи в каталоге | | (рабочем индексе)) | | { | | получить номер индекса для совпавшей компонен-| | ты; | | если (найденный индекс является индексом кор- | | ня и рабочий индекс является индексом корня | | и имя компоненты "..") | | { | | /* пересечение точки монтирования */ | | получить запись в таблице монтирования для | | рабочего индекса; | | освободить рабочий индекс (алгоритм iput); | | рабочий индекс = индексу точки монтирования;| | заблокировать индекс точки монтирования; | | увеличить значение счетчика ссылок на рабо- | | чий индекс; | | перейти к поиску компоненты (для ".."); | | } | | освободить рабочий индекс (алгоритм iput); | | рабочий индекс = индексу с новым номером | | (алгоритм iget); | | } | | в противном случае /* компонента отсутствует в | | каталоге */ | | возвратить (нет индекса); | | } | | возвратить (рабочий индекс); | | } | +------------------------------------------------------------+ Рисунок 5.26. Модификация алгоритма синтаксического анализа имени файла компонента пути поиска, в свою очередь, имеет имя "..", ядро идентифицирует индекс как точку монтирования. Оно находит в таблице монтирования запись, 117 номер устройства в которой совпадает с номером устройства для последнего из найденных индексов, получает индекс для каталога, в котором производится монтирование, и продолжает поиск компоненты с именем "..", используя только что полученный индекс в качестве рабочего. В корне файловой системы, тем не менее, корневым каталогом является "..". В вышеприведенном примере (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 завершается неудачно: если бы она прошла успешно, активные файлы сделались бы