из канала, ни проявлять по этому поводу какое-либо беспокойство.
Процесс, выполняющий программу, которая приведена на Рисунке 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

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