The design of the unix operating system by Maurice J

Вид материалаРеферат
5.7 Создание файла
5.8 Создание специальных файлов
5.9 Смена текущего и корневого каталога
Подобный материал:
1   ...   9   10   11   12   13   14   15   16   ...   55

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

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

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

position = lseek(fd,offset,reference);

где fd - дескриптор файла, идентифицирующий файл, offset - смеще-

ние в байтах, а reference указывает, является ли значение offset

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

вода или смещением от конца файла. Возвращаемое значение,

position, является смещением в байтах до места, где будет начи-

наться следующая операция чтения или записи. Например, в програм-

ме, приведенной на Рисунке 5.10, процесс открывает файл, считыва-

ет байт, а затем вызывает функцию lseek, чтобы заменить значение

поля смещения в таблице файлов величиной, равной 1023 (с перемен-

ной reference, имеющей значение 1), и выполняет цикл. Таким обра-

зом, программа считывает каждый 1024-й байт файла. Если reference

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

2, ядро ведет поиск от конца файла. Функция lseek ничего не долж-

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

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

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

таблицы файлов; в последующих вызовах функций read и write смеще-

ние из таблицы файлов используется в качестве начального смеще-

ния.


5.6 CLOSE


Процесс закрывает открытый файл, когда процессу больше не

нужно обращаться к нему. Синтаксис вызова системной функции close

(закрыть):


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

│ #include

│ main(argc,argv) │

│ int argc; │

│ char *argv[]; │

│ { │

│ int fd,skval; │

│ char c; │

│ │

│ if(argc != 2) │

│ exit(); │

│ fd = open(argv[1],O_RDONLY); │

│ if (fd == -1) │

│ exit(); │

│ while ((skval = read(fd,&c,1)) == 1) │

│ { │

│ printf("char %c\n",c); │

│ skval = lseek(fd,1023L,1); │

│ printf("new seek val %d\n",skval); │

│ } │

│ } │

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


Рисунок 5.10. Программа, содержащая вызов системной функции

lseek


close(fd);

где fd - дескриптор открытого файла. Ядро выполняет операцию зак-

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

записей в таблице файлов и таблице индексов. Если счетчик ссылок

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

тем, что были обращения к функциям dup или fork, то это означает,

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

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

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

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

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

open (алгоритм iput). Если другие процессы все еще ссылаются на

индекс, ядро уменьшает значение счетчика ссылок на индекс, но ос-

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

для переназначения, так как его счетчик ссылок содержит 0. Когда

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

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

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

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

результате выполнения другой системной функции. Когда процесс за-

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

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

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

тым после своего завершения.

На Рисунке 5.11, например, показаны записи из таблиц,

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

вает соответствующие им файлы. Записи, соответствующие дескрипто-

рам 3 и 4 в таблице пользовательских дескрипторов файлов, пусты.

Счетчики в записях таблицы файлов теперь имеют значение 0, а сами

записи пусты. Счетчики ссылок на файлы "/etc/passwd" и "private"

в индексах также уменьшились. Индекс для файла "private" находит-

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

равен 0, но запись о нем не пуста. Если еще какой-нибудь процесс


пользовательские дескрип-

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

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

0│ │ │ │ │ │

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

1│ │ │ │ │ │

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

2│ │ │ │ │ │

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

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

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

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

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

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

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

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

│ │ ││ │ 1 │ │ │ │

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

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

││ │ │ │ │ │

----------┐ ││ │ │ │ │ │

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

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

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

+---------+ │ │ 1 │ ││ │ │

2│ │ │ +------------+ ││ │ │

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

3│ NULL │ │ │ │ ││ │ счет- │

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

4│ NULL │ │ │ │ │ │ 1 │

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

5│ │ │ │ │ │ │ │

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

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

│ │ │ │ чик │ │ │ │

│ │ │ │ 0 │ │ │ │

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

│ │ │ │ │ │

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

│ │ │ │ │ счет- │

│ +------------+ │ │ чик (private)│

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

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

│ 1 │ │ │

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

│ │ │ │

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

│ │

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

│ счет- │

│ чик │

│ 0 │

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


Рисунок 5.11. Таблицы после закрытия файла


обратится к файлу "private", пока индекс еще находится в списке

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

в разделе 4.1.2.


5.7 СОЗДАНИЕ ФАЙЛА


Системная функция open дает процессу доступ к существующему

файлу, а системная функция creat создает в системе новый файл.

Синтаксис вызова системной функции creat:

fd = creat(pathname,modes);


где переменные pathname, modes и fd имеют тот же смысл, что и в

системной функции open. Если прежде такого файла не существовало,

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

доступа к нему; если же такой файл уже существовал, ядро усекает

файл (освобождает все существующие блоки данных и устанавливает

размер файла равным 0) при наличии соответствующих прав доступа к

нему (***). На Рисунке 5.12 приведен алгоритм создания файла.


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

│ алгоритм creat │

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

│ установки прав доступа к файлу │

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

│ { │

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

│ если (файл уже существует) │

│ { │

│ если (доступ не разрешен) │

│ { │

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

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

│ } │

│ } │

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

│ { │

│ назначить свободный индекс из файловой системы (алго- │

│ ритм ialloc); │

│ создать новую точку входа в родительском каталоге: │

│ включить имя нового файла и номер вновь назначенного │

│ индекса; │

│ } │

│ выделить для индекса запись в таблице файлов, инициализи-│

│ ровать счетчик; │

│ если (файл существовал к моменту создания) │

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

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

│ возвратить (пользовательский дескриптор файла); │

│ } │

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


Рисунок 5.12. Алгоритм создания файла


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

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

речь идет о разборе имен каталогов. Однако, когда дело касается

последней компоненты имени пути поиска, а именно идентификатора

создаваемого файла, namei отмечает смещение в байтах до первой


---------------------------------------

(***) Системная функция open имеет два флага, O_CREAT (создание)

и O_TRUNC (усечение). Если процесс устанавливает в вызове

функции флаг O_CREAT и файл не существует, ядро создаст

файл. Если файл уже существует, он не будет усечен, если

только не установлен флаг O_TRUNC.


пустой позиции в каталоге и запоминает это смещение в пространс-

тве процесса. Если ядро не обнаружило в каталоге компоненту имени

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

что найденную пустую позицию. Если в каталоге нет пустых позиций,

ядро запоминает смещение до конца каталога и создает новую пози-

цию там. Оно также запоминает в пространстве процесса индекс

просматриваемого каталога и держит индекс заблокированным; ката-

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

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

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

вать. Оно проверяет наличие у процесса разрешения на запись в ка-

талог. Поскольку процесс будет производить запись в каталог в ре-

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

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

талоге.

Предположив, что под данным именем ранее не существовало фай-

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

(раздел 4.6). Затем оно записывает имя нового файла и номер вновь

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

сохраняет в пространстве процесса. Впоследствии ядро освобождает

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

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

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

вновь выделенный индекс на диск (алгоритм bwrite), прежде чем за-

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

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

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

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

мально. Если, с другой стороны, каталог был записан раньше вновь

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

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

индекс (более подробно об этом см. в разделе 5.16.1).

Если данный файл уже существовал до вызова функции creat, яд-

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

файл должен позволять процессу производить запись в него, чтобы

можно было создать "новый" файл с тем же самым именем, так как

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

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

free, так что файл будет выглядеть как вновь созданный. Тем не

менее, владелец и права доступа к файлу остаются прежними: ядро

не передает право собственности на файл владельцу процесса и иг-

норирует права доступа, указанные процессом в вызове функции. На-

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

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

меняет содержимого каталога.

Функция creat продолжает работу, выполняя тот же алгоритм,

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

лице файлов, чтобы процесс мог читать из файла, а также запись в

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

возвращает указатель на последнюю запись в виде пользовательского

дескриптора файла.


5.8 СОЗДАНИЕ СПЕЦИАЛЬНЫХ ФАЙЛОВ


Системная функция mknod создает в системе специальные файлы,

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

и каталоги. Она похожа на функцию creat в том, что ядро выделяет

для файла индекс. Синтаксис вызова системной функции mknod:

mknod(pathname,type and permissions,dev)

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

файловой системы, type and permissions - тип вершины (например,

каталог) и права доступа к создаваемому файлу, а dev указывает

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

циальных файлов (глава 10). На Рисунке 5.13 приведен алгоритм,

реализуемый функцией mknod при создании новой вершины.


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

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

│ входная информация: вершина (имя файла) │

│ тип файла │

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

│ старший, младший номера устройства │

│ (для блочных и символьных специальных │

│ файлов) │

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

│ { │

│ если (новая вершина не является поименованным каналом │

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

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

│ получить индекс вершины, являющейся родительской для │

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

│ если (новая вершина уже существует) │

│ { │

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

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

│ } │

│ назначить для новой вершины свободный индекс из файловой│

│ системы (алгоритм ialloc); │

│ создать новую запись в родительском каталоге: включить │

│ имя новой вершины и номер вновь назначенного индекса; │

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

│ iput); │

│ если (новая вершина является блочным или символьным спе-│

│ циальным файлом) │

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

│ са; │

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

│ } │

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


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


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

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

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

файла и номер индекса в родительский каталог. Оно устанавливает

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

каналом, каталогом или специальным файлом. Наконец, если файл яв-

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

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

Если функция mknod создает каталог, он будет существовать по за-

вершении выполнения функции, но его содержимое будет иметь невер-

ный формат (в каталоге будут отсутствовать записи с именами "." и

".."). В упражнении 5.33 рассматриваются шаги, необходимые для

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


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

│ алгоритм смены каталога │

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

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

│ { │

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

│ namei); │

│ если (индекс не является индексом каталога или же про- │

│ цессу не разрешен доступ к файлу) │

│ { │

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

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

│ } │

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

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

│ iput); │

│ поместить новый индекс в позицию для текущего каталога │

│ в пространстве процесса; │

│ } │

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


Рисунок 5.14. Алгоритм смены текущего каталога


5.9 СМЕНА ТЕКУЩЕГО И КОРНЕВОГО КАТАЛОГА


Когда система загружается впервые, нулевой процесс делает

корневой каталог файловой системы текущим на время инициализации.

Для индекса корневого каталога нулевой процесс выполняет алгоритм

iget, сохраняет этот индекс в пространстве процесса в качестве

индекса текущего каталога и снимает с индекса блокировку. Когда

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

кущий каталог старого процесса в своем адресном пространстве, а

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

дексе.

Алгоритм chdir (Рисунок 5.14) изменяет имя текущего каталога

для процесса. Синтаксис вызова системной функции chdir:

chdir(pathname);

где pathname - каталог, который становится текущим для процесса.

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

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

цесса право доступа к каталога. Ядро снимает с нового индекса

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

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

текущего каталога (алгоритм iput), хранящийся в пространстве

процесса, и запоминает в этом пространстве новый индекс. После

смены процессом текущего каталога алгоритм namei использует ин-

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

которые не берут начало от корня. По окончании выполнения систем-

ной функции chdir счетчик ссылок на индекс нового каталога имеет

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

кущего каталога может стать равным 0. В этом отношении функция

chdir похожа на функцию open, поскольку обе функции обращаются к

файлу и оставляют его индекс в качестве выделенного. Индекс, вы-

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

тогда, когда процесс меняет текущий каталог еще раз или когда

процесс завершается.

Процессы обычно используют глобальный корневой каталог файло-

вой системы для всех имен путей поиска, начинающихся с "/". Ядро

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

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

мы. Процессы могут менять свое представление о корневом каталоге

файловой системы с помощью системной функции chroot. Это бывает

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

ческой структуры файловой системы и запустить процессы там. Син-

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

chroot(pathname);

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

ся ядром в качестве корневого каталога для процесса. Выполняя

функцию chroot, ядро следует тому же алгоритму, что и при смене

текущего каталога. Оно запоминает индекс нового корня в прост-

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

полнения функции. Тем не менее, так как умолчание на корень для

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

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

мо или процесс-предок исполнят вызов функции chroot. Новый индекс

становится логическим корнем файловой системы для процесса (и