The design of the unix operating system by Maurice J

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

│ cp2 = buf; │

│ while(*cp1) │

│ *cp2++ = *cp1++; │

│ pipe(fds); │

│ for (;;) │

│ { │

│ write(fds[1],buf,6); │

│ read(fds[0],buf,6); │

│ } │

│ } │

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


Рисунок 5.18. Чтение из канала и запись в канал


5.12.5 Примеры


Программа на Рисунке 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); │

│ } │

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


Рисунок 5.19. Чтение и запись в поименованный канал


тоянно записывает в канал строку символов "hello"; будучи запущен

без второго аргумента, он ведет чтение из поименованного канала.

Два процесса запускаются по одной и той же программе, тайно дого-

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

"fifo", но им нет необходимости быть родственными процессами.

Другие пользователи могут выполнять программу и участвовать в ди-

алоге (или мешать ему).


5.13 DUP


Системная функция dup копирует дескриптор файла в первое сво-

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

ращая новый дескриптор пользователю. Она действует для всех типов

файла. Синтаксис вызова функции:

newfd = dup(fd);

где fd - дескриптор файла, копируемый функцией, а newfd - новый

дескриптор, ссылающийся на файл. Поскольку функция dup дублирует

дескриптор файла, она увеличивает значение счетчика в соответс-

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

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

теперь стало на одну больше. Например, обзор структур данных,

изображенных на Рисунке 5.20, показывает, что процесс вызывает

следующую последовательность функций: он открывает (open) файл с

именем "/etc/passwd" (файловый дескриптор 3), затем открывает

файл с именем "local" (файловый дескриптор 4), снова файл с име-

нем "/etc/passwd" (файловый дескриптор 5) и, наконец, дублирует

(dup) файловый дескриптор 3, возвращая дескриптор 6.


таблица пользова-

тельских дескрип-

торов файла таблица файлов таблица индексов

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

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

+---------+ │ │ │ │ │

1│ ----+---┐L-->│ │ │ │

+---------+ │ +------------+ │ │

2│ ----+--┐L--->│ │ │ │

+---------+ L---->│ │ │ │

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

+---------+ │ │ │ +--------------+

4│ ----+---┐│ │ │ ----->│ счет- │

+---------+ ││ │ │ │ │ чик (/etc/ │

5│ ----+--┐││ +------------+ │ --->│ 2 passwd)│

+---------+ │││ │ счет- │ │ │ +--------------+

6│ ----+┐ ││L-->│ чик +--- │ │ │

+---------+L-││--->│ 2 │ │ │ │

7│ │ ││ +------------+ │ │ │

+---------+ ││ │ │ │ │ │

│ │ ││ │ │ │ │ │

│ │ ││ │ │ │ │ │

│ │ ││ +------------+ │ │ │

L---------- ││ │ счет- │ │ │ │

│L--->│ чик +----│┐ │ │

│ │ 1 │ ││ │ │

│ +------------+ ││ │ │

│ │ │ ││ +--------------+

│ │ │ ││ │ счет- │

│ │ │ │L->│ чик (local)│

│ │ │ │ │ 1 │

│ │ │ │ +--------------+

│ │ │ │ │ │

│ +------------+ │ │ │

│ │ счет- │ │ │ │

L---->│ чик +----- │ │

│ 1 │ │ │

+------------+ │ │

│ │ │ │

│ │ L---------------

│ │

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


Рисунок 5.20. Структуры данных после выполнения функции dup


Возможно, 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)); │

│ } │

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


Рисунок 5.21. Программа на языке Си, иллюстрирующая использо-

вание функции dup


открывает один и тот же файл дважды и читает дважды одни и те же

данные (раздел 5.2). Процесс может освободить с помощью функции

close любой из файловых дескрипторов по своему желанию, и

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

как показано на примере. В частности, процесс может "закрыть"

дескриптор файла стандартного вывода (файловый дескриптор 1),

снять с него копию, имеющую то же значение, и затем рассматривать

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

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

pipe и dup при описании особенностей реализации командного про-

цессора.


5.14 МОНТИРОВАНИЕ И ДЕМОНТИРОВАНИЕ ФАЙЛОВЫХ СИСТЕМ


Физический диск состоит из нескольких логических разделов, на

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

ответствует файл устройства, имеющий определенное имя. Процессы

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

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

ляя его себе в виде последовательности дисковых блоков. Это взаи-

модействие во всех деталях рассматривается в главе 10. Раздел

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

блока начальной загрузки, суперблока, списка индексов и информа-

ционных блоков (см. главу 2). Системная функция mount (монтиро-

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

существующей иерархией файловых систем, а функция umount (демон-

тировать) выключает файловую систему из иерархии. Функция mount,

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

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

тельности дисковых блоков.

Синтаксис вызова функции mount:

mount(special pathname,directory pathname,options);

где special pathname - имя специального файла устройства, соот-

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

directory pathname - каталог в существующей иерархии, где будет

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

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

вую систему "только для чтения" (при этом не будут выполняться


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

/

│ │ │

-----------------T--+-------------┐ Корневая

│ │ │ │ │ файловая

bin etc usr система

│ │ │ │

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

│ │ │ │ │ │ │


cc date sh getty passwd

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

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

/

│ │ │

Файловая -----------------T--+-------------┐

система из │ │ │ │ │

раздела с bin include src

именем │ │ │ │ │

/dev/dsk1 ------+-----┐ │ │

│ │ │ │ │ │ │

awk banner yacc stdio.h uts

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


Рисунок 5.22. Дерево файловых систем до и после выполнения

функции mount


такие функции, как write и creat, которые производят запись в

файловую систему). Например, если процесс вызывает функцию mount

следующим образом:

mount("/dev/dsk1","/usr",0);

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

ле с именем "/dev/dsk1", к каталогу "/usr" в существующем дереве

файловых систем (см. Рисунок 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); │

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

│ } │

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


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


На Рисунке 5.23 показан алгоритм монтирования файловой систе-

мы. Ядро позволяет монтировать и демонтировать файловые системы


только тем процессам, владельцем которых является суперпользова-

тель. Предоставление возможности выполнять функции mount и umount

всем пользователям привело бы к внесению с их стороны хаоса в ра-

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

татом неосторожности. Суперпользователи могут разрушить систему

только случайно.

Ядро находит индекс специального файла, представляющего фай-

ловую систему, подлежащую монтированию, извлекает старший и млад-

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

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

смонтирована. Счетчик ссылок в индексе каталога должен иметь зна-

чение, не превышающее 1 (и меньше 1 он не должен быть - почему

?), в связи с наличием потенциально опасных побочных эффектов

(см. упражнение 5.27). Затем ядро назначает свободное место в

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

ет значение полю номера устройства в таблице. Вышеуказанные наз-


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

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

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

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

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

пользования в двух вызовах функции mount одной и той же записи

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

может воспрепятствовать повторному монтированию одной и той же

системы другими процессами, которое, будь оно допущено, могло бы

привести к непредсказуемым последствиям (см. упражнение 5.26).

Ядро вызывает процедуру открытия для блочного устройства, со-

держащего файловую систему, точно так же, как оно делает это при

непосредственном открытии блочного устройства (глава 10). Проце-

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

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

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

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

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

суперблок, используя один из вариантов алгоритма read. Ядро сох-

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

ма, давая возможность маршрутам поиска файловых имен, содержащих

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

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

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

пользователя, место (точка) монтирования и корень файловой систе-

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

благодаря их сосуществованию в одной записи таблицы монтирования.

Процессы больше не могут обращаться к индексу каталога - точки

монтирования.

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

щая поля для списка свободных блоков и списка свободных индексов

и устанавливая число свободных индексов в суперблоке равным 0.

Целью инициализации (задания начальных значений полей) является

сведение к минимуму опасности разрушить файловую систему, если

монтирование осуществляется после аварийного завершения работы

системы. Если ядро заставить думать, что в суперблоке отсутствуют

свободные индексы, то это приведет к запуску алгоритма ialloc,

ведущего поиск на диске свободных индексов. К сожалению, если

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

этот список изнутри (см. раздел 5.17 о сопровождении файловой

системы). Если пользователь монтирует файловую систему только для

чтения, запрещая проведение всех операций записи в системе, ядро

устанавливает в суперблоке соответствующий флаг. Наконец, ядро

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

процессы позднее могли ссылаться на нее. На Рисунке 5.24 предс-

тавлен вид различных структур данных по завершении выполнения

функции mount.


5.14.1 Пересечение точек монтирования в маршрутах поиска

имен файлов


Давайте повторно рассмотрим поведение алгоритмов namei и iget

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

тирования. Точку монтирования можно пересечь двумя способами: из

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

тему, которая монтируется (в направлении от глобального корня к

листу), и в обратном направлении. Эти способы иллюстрирует следу-

ющая последовательность команд shell'а.


Таблица индексов Таблица монтирования

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

│ │ │ │

+------------------+ │ │

│ Индекс каталога, + - - ┐ │ │

│ где производится │ │ │

│ монтирование │ │ │ │ --------┐

│ Помечен как "точ-│<---┐ │ │-->│ Буфер │

│ ка монтирования" │ ││ │ ││ L--------

│ Счетчик ссылок =1│ │ │ ││

+------------------+ │L >+--------------------+│

│ │ │ │ Суперблок ---+-

+------------------+ L---+ Индекс точки монти-│

│ Индекс устройства│ │ рования │

│ Не используется │ ----+- Корневой индекс │

│ Счетчик ссылок =0│ │ +--------------------+

+------------------+ │ │ │

│ │ │ │ │

+------------------+<---- │ │

│ Индекс корня мон-│ │ │

│ тируемой файловой│ │ │

│ системы │ │ │

│ Счетчик ссылок =1│ L---------------------

+------------------+

│ │

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


Рисунок 5.24. Структуры данных после монтирования


mount /dev/dsk1 /usr

cd /usr/src/uts

cd ../../..

По команде mount после выполнения некоторых логических прове-

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

вую систему в дисковом разделе с именем "/dev/dsk1" под управле-

нием каталога "/usr". Первая из команд cd (сменить каталог)

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

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

секающего точку монтирования в "/usr". Вторая из команд cd приво-

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