Управление файлами

ОС UNIX поддерживает операции ввода-вывода с помощью набора взаимосвязанных таблиц. Основной из них считается таблица описателей файлов (FDT). Она представляет собой хранящуюся в оперативной памяти ЭВМ структуру данных, элементами которой являются копии описателей файлов, к которым была осуществлена попытка доступа. Каждому элементу таблицы описателей файлов обязательно соответствует один или несколько элементов системной таблицы файлов (SFT). Элемент таблицы файлов содержит информацию о режиме открытия файла и положении указателя чтения-записи. Таким образом, каждый файл может быть одновременно открыт несколькими независимыми процессами, и при каждом открытии файла количество элементов таблицы файлов будет увеличиваться на единицу.

Если процессы связаны родственными отношениями, то процесс-потомок, порожденный системным вызовом fork, унаследует все открытые файлы процесса-предка. Это осуществляется с помощью таблицы открытых файлов процесса, которая создается сразу после порождения процесса, содержит информацию только о файлах, открытых данным процессом и передается процессу-потомку в момент его порождения.

Для примера рассмотрим следующую последовательность системных вывозов:

fd1 = open("/etc/passwd",O_RDONLY);

fd2 = open("local",O_RDWR);

fd3 = open("/etc/passwd",O_WRONLY);

На Рис. 3 показана взаимосвязь между таблицей описателей файлов, таблицей файлов и таблицей открытых файлов процесса. Каждый вызов функции open возвращает процессу дескриптор файла, а соответствующая запись в таблице открытых файлов процесса указывает на уникальную запись в таблице файлов ядра, даже если один и тот же файл ("/etc/passwd") открывается дважды. Записи в таблице файлов для всех экземпляров одного и того же открытого файла указывают на одну запись в таблице описателей файлов, хранящихся в памяти. Процесс может обращаться к файлу "/etc/passwd" с чтением или записью, но только через дескрипторы файла, имеющие значения 3 и 5 (Рис.3). Ядро запоминает разрешение на чтение или запись в файл в строке таблицы файлов, выделенной во время выполнения функции open.

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

Предположим, что другой процесс выполняет следующий набор операторов:

fd1 = open("/etc/passwd",O_RDONLY);

fd2 = open("private",O_RDONLY);

На Рис. 4 показана взаимосвязь между соответствующими структурами данных, когда оба процесса (и больше никто) имеют открытые файлы. Снова результатом каждого вызова функции open является выделение уникальной точки входа в таблице открытых файлов процесса и в таблице файлов ядра, и ядро хранит не более одной записи на каждый файл в таблице описателей файлов, размещенных в памяти.

Рис. 4. Структуры данных после того, как два независимых процесса открыли файлы

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

Вышеприведенные примеры показывают взаимосвязь между записями таблицы открытых файлов процесса и записями в таблице файлов ядра типа “один к одному”. Однако, таблица файлов, реализованная как отдельная структура, позволяет совместно использовать один и тот же указатель смещения нескольким пользовательским дескрипторам файла. В системных вызовах dup (см. раздел "Программирование операций ввода-вывода") и fork (лабораторная работа №3) при работе со структурами данных допускается такое совместное использование.

Первые три пользовательских дескриптора (0, 1 и 2) именуются дескрипторами файлов стандартного ввода, стандартного вывода и стандартного потока ошибок. Процессы в системе UNIX по договоренности используют дескриптор файла стандартного ввода при чтении вводимой информации, дескриптор файла стандартного вывода при записи выводимой информации и дескриптор стандартного файла ошибок для записи сообщений об ошибках. В операционной системе нет никакого указания на то, что эти дескрипторы файлов являются специальными. Группа пользователей может условиться о том, что файловые дескрипторы, имеющие значения 4, 6 и 11, являются специальными, но более естественно начинать отсчет с 0 (как в языке Си). Принятие соглашения сразу всеми пользовательскими программами облегчит связь между ними при использовании каналов.

Обычно операторский терминал служит и в качестве стандартного ввода, и в качестве стандартного вывода, и в качестве стандартного устройства вывода сообщений об ошибках.

Вызов fork порождает процесс, являющийся потомком по отношению к тому процессу, из которого осуществлен вызов. Процесс-потомок является точной копией процесса-предка за исключением номера самого процесса и значения, возвращаемого вызовом fork. При этом потомок получает к ранее открытым файлам доступ того же типа, что и предок (говорят, что процесс наследует открытые файлы). Родственные процессы общаются с общим файлом через один указатель чтения/записи, и если один из процессов прочитал или записал данные в файл, то значение указателя чтения/записи изменится для всех родственных процессов, имеющих доступ к этому файлу. Естественно, это не относится к файлам, которые были открыты родственными процессами после вызова fork: в этом случае каждый процесс обращается к файлу через собственный указатель.

Для примера рассмотрим следующую последовательность системных вызовов и состояние файловых таблиц после их выполнения (Рис.5):

fd1 = open("/etc/passwd",O_RDONLY);

pid = fork();

fd2 = open("private",O_RDWR);

Первый вызов open выполняется до вызова fork, он создает записи, относящиеся к файлу "/etc/passwd", во всех файловых таблицах. При выполнении вызова fork процесс-потомок получает копию таблицы открытых файлов процесса, а в записях таблиц файлов и описателей файлов счетчики ссылок на файл "/etc/passwd" увеличиваются на единицу и становятся равным 2.

Рис. 5. Структуры данных после того, как два родственных процесса открыли файлы

Дополнительная запись в таблицу файлов не добавляется, оба процесса имеют доступ к файлу через один указатель чтения/записи. Второй вызов open выполняется после вызова fork, то есть тогда, когда существуют уже два процесса – предок и потомок. Каждый из них выполняет открытие файла независимо от другого, поэтому новые записи добавляются во все таблицы (запись в таблице описателей одна на файл, но счетчик ссылок на файл равен числу открывших файл процессов).

Закрытие файла уменьшает число ссылок на файл, и только когда оно становится равным 0, происходит удаление соответствующих записей из таблиц.


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: