Файл: Лекция 6 Ввод-Вывод и файловая система.doc

ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 18.06.2021

Просмотров: 2292

Скачиваний: 56

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.







Файловые операции


Два способа организации файловых операций


Файловая система ОС должна предоставлять пользователям набор операций работы с файлами, оформленный в виде системных вызовов. Этот набор обычно состоит из таких системных вызовов, как creat (создать файл), read (читать из файла), write (записать в файл) и некоторых других.

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

Какие бы операции не выполнялись над файлом, ОС необходимо выполнить ряд универсальных для всех операций действий:

1. По символьному имени файла найти его характеристики, которые хранятся в файловой системе на диске.

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

3. На основании характеристик файла проверить права пользователя на выполнение запрошенной операции (чтение, запись, удаление, просмотр атрибутов файла).

4. Очистить область памяти, отведенную под временное хранение характеристик файла.

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

Операционная система может выполнять последовательность действий над файлом двумя способами (рис. 7.26):

 Для каждой операции выполняются как универсальные, так и уникальные действия. Такая схема иногда называется схемой без запоминания состояния операций (stateless).

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

Подавляющее большинство файловых систем поддерживает второй способ организации файловых операций как более экономичный и быстрый. Первый способ обладает одним преимуществом — он более устойчив к сбоям в работе системы, так как каждая операция является самодостаточной и не зависит от результата предыдущей. Поэтому первый способ иногда применяется в распределенных сетевых файловых системах (например, в Network File System, NFS компании Sun), когда сбои из-за потерь пакетов или отказов одного из сетевых узлов более вероятны, чем при локальном доступе к файлам.

Рис. 7.26. Два способа выполнения файловых операций

При втором способе в файловой системе вводятся два специальных системных вызова: open — открытие файла, и close — закрытие файла.

Системный вызов открытия файла open выполняется перед началом любой последовательности операций с файлом, а вызов закрытия файла close — после окончания работы с файлом. Основной задачей вызова open является преобразование символьного имени файла в его уникальное числовое имя, копирование характеристик файла из дисковой области в буфер оперативной памяти и проверка прав пользователя на выполнение запрошенной операции. Вызов close освобождает буфер с характеристиками файла и делает невозможным продолжение операций с файлом без его повторного открытия.


Операции открытия и закрытия файла в той или иной форме утвердились в операционных системах очень давно. Даже в такой «старой» операционной системе, как OS/360, существовала макрокоманда OPEN, по которой в специальном буфере, называемом DCB (Data Control Block), собирались из различных источников все нужные характеристики набора данных (понятие, близкое к современному понятию файла), используемые затем при выполнении операций чтения и записи.

Далее основные системные вызовы файловых операций рассматриваются более детально на примере их реализации в ОС UNIX, в которой они приобрели тот вид, который сегодня поддерживается практически всеми операционными системами.

 

Открытие файла


Системный вызов open в ОС UNIX работает с двумя аргументами: символьным именем открываемого файла и режимом открытия файла. Режим открытия говорит системе, какие операции будут выполняться над файлом в последовательности операций до закрытия файла по системному вызову close, например: только чтение, только запись или чтение и запись.

При открытии файла ОС сначала выполняет преобразование первого аргумента системного вызова, то есть символьного имени файла, в его уникальное числовое имя, которым в традиционных файловых системах UNIX является номер индексного дескриптора. Эта процедура была рассмотрена выше при описании файловой системы s5.

По номеру индексного дескриптора inode файловая система находит нужную запись на диске и копирует из нее характеристики файла в оперативную память.

Для хранения копии индексного дескриптора используются буферные области системного виртуального пространства. Характеристики индексного дескриптора, перенесенные в оперативную память, помещаются в структуру так называемого виртуального дескриптора vnode (virtual node). Структура vnode включает поля индексного дескриптора файла inode, а также несколько перечисленных ниже дополнительных полей, полезных при выполнении операций с файлом.

 Состояние индексного дескриптора в памяти, отражающее: 

 заблокирован ли файл; 

 ждет ли снятия блокировки с файла какой-либо процесс; 

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

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

 является ли файл точкой монтирования. 

 Логический номер устройства файловой системы, содержащей файл.

 Номер индексного дескриптора. В дисковом индексном дескрипторе это поле отсутствует, так как номер определяется положением дескриптора относительно начала области индексных дескрипторов.

 Счетчик ссылок на данную структуру vnode.

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


Использование единственной копии характеристик файла и некоторых характеристик файловых операций (например, признака блокировки), общих для всех работающих с файлом процессов, экономит системную память. Тем не менее существуют характеристики, индивидуальные для каждого процесса, выполняющего некоторую последовательность операций с определенным файлом. Для их хранения в UNIX используется структура типа file, которая так же, как и vnode, хранится в системной области памяти.

При каждом открытии процессом файла ОС проверяет права пользовательского процесса на выполнение запрошенной операции с файлом и, если проверка прошла успешно, создает в системной области памяти новую структуру f i I e, которая описывает как открытый файл, так и операции, которые процесс собирается производить с файлом (например, чтение).

Структура file содержит такие поля, как:

 признак режима открытия (только для чтения, для чтения и записи и т. п.);

  указатель на структуру vnode;

 текущее смещение в файле (переменная offset) при операциях чтения/записи; О счетчик ссылок на данную структуру;

указатель на структуру, содержащую права процесса, открывшего файл (эта структура находится в дескрипторе процесса);

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

Переменная offset, хранящаяся в структуре file, позволяет ОС запоминать текущее положение условного указателя в последовательности байт файла. При открытии файла эта переменная указывает на начальный или конечный байт файла в зависимости от заданного режима открытия. После выполнения операций чтения или записи указатель сдвигается на то количество байт, которое было прочитано или записано в результате операции. Следующая операция застает указатель в том состоянии, в котором его оставила предыдущая операция. Прикладной программист может явно управлять положением указателя с помощью системного вызова lseek, который будет рассмотрен ниже.

При каждом новом открытии какого-либо файла ОС создает новую структуру file и помещает ее в дважды связанный список (рис. 7.27). Обычно под хранение структур file в системной области отводится ограниченная область, поэтому общее количество открытых файлов всеми процессами в любой момент ограничено.

После создания структуры file операционная система помещает указатель на нее в таблицу открытых файлов процесса, которая находится в контексте процесса. Если процесс несколько раз открывает один и тот же файл, то структура file создается для каждой операции открытия. Так как контекст процесса-родителя в UNIX наследуется процессом-потомком, то потомок наследует и указатели на все открытые родителем файлы, получая возможность выполнять над ними операции.

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


После открытия файла его дескриптор используется во всех дальнейших операциях с файлом вплоть до явного закрытия файла. Таким образом, дескриптор файла является временным уникальным именем, но не файла, а определенной последовательности операций с этим файлом.

Для открытия файла /bin/prog1.ехе в режиме «только для чтения» прикладной программист может использовать следующее выражение на языке С:

fd =open("'/bin/progl.exe". 0_RDONLY):

Здесь fd — это целочисленная переменная, сохраняющая значение дескриптора открытого файла. Ее значение должно использоваться в операциях обмена данными с файлом /bin/prog1 .exe. При неудачной попытке открытия файла (нет прав для выполнения затребованной операции, неверное имя файла) переменной fd присваивается значение -1, которое является индикатором ошибки для всех системных вызовов UNIX.

Рис. 7.27. Связь процесса с открытыми файлами


Обмен данными с файлом


Для обмена данными с предварительно открытым файлом в ОС UNIX существуют системные вызовы read и write. В том случае, когда необходимо явным образом указать, с какого байта файла необходимо читать или записывать данные, используется также системный вызов  lseek.

Системный вызов чтения данных из файла read имеет три аргумента:

read(fd,buffer,nbytes);

Первый аргумент fd является целочисленной переменной, имеющей значение дескриптора открытого файла. Второй аргумент buffer является указателем на область пользовательской памяти, в которую система должна поместить считанные данные. Количество байт этой области памяти задается третьим целочисленным аргументом nbytes. Функция read возвращает действительное количество считанных байт (оно может отличаться от заданного, если, например, была задана область чтения, выходящая за пределы файла) или же код ошибки -1.

Начало дисковой области, которую нужно прочитать с помощью вызова read, явно в этом системном вызове не указывается. Чтение начинается с того байта, на который указывает смещение offset в структуре file. На это смещение указывает запись с номером fd в таблице открытых файлов процесса. После выполнения вызова read смещение offset наращивается на количество прочитанных байт.

Вид системного вызова записи данных write аналогичен вызову read:

write(fd.buffer.nbytes):

Функция write записывает nbytes из буфера оперативной памяти buffer в файл, описываемый дескриптором fd. Функция write, так же как и read, возвращает вызвавшей ее программе значение реально переданных ею байт или код ошибки.

Рассмотрим пример, в котором прикладная программа работает с файлом, состоящем из записей фиксированной длины в 50 байт:

fd =open("/doc/qwery/basel2.txt". 0_RDWR):

readCfd.bufferl.50):

read(fd.buffer2.2500):

lseekCfd,150.0): write (fd.output. 300):

 close(fd):

В приведенном фрагменте программы после открытия файла /doc/query/base12.txt для чтения и записи выполняется чтение первой записи файла, а затем читается область файла, включающая еще 50 записей, начиная со 2 по 51. После обработки считанных записей (эти инструкции опущены) производятся перемещение указателя смещения в файле на начало четвертой записи и запись результатов в шесть последовательных записей, начиная с четвертой. Завершается фрагмент закрытием файла с помощью системного вызова close.


Все описанные системные вызовы являются синхронными, то есть пользовательский процесс переводится в состояние ожидания до тех пор, пока операция ввода-вывода не завершится.

Описанный набор системных вызовов, появившийся в ОС UNIX еще в 70-х годах, стал стандартом де-факто для современных операционных систем. Эти традиционные системные вызовы часто в конкретных ОС дополняются оригинальными системными вызовами ввода-вывода, например операциями асинхронного типа. На основе системных вызовов ввода-вывода обычно строятся более мощные библиотечные функции ввода-вывода, составляющие прикладной интерфейс ОС.





Блокировки файлов


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

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

Многопользовательские операционные системы обычно поддерживают специальный системный вызов, позволяющий программисту установить и проверить блокировки на файл и его отдельные области. В UNIX такой системный вызов называется fcntl. В его аргументах указывается дескриптор файла, для которого нужно установить или проверить блокировки, тип операции (блокирование или проверка, блокирование доступа для чтения или для записи), а также область блокирования — смещение от начала файла и размер в байтах.

При проверке наличия блокировок, установленных другими процессами, вызов fcntl немедленно возвращает управление с сообщением результата. При установке блокировки можно задать два режима работы системного вызова: с переходом процесса в состояние ожидания в том случае, если блокировку установить невозможно (синхронный системный вызов), и с немедленным возвратом в такой ситуации с сообщением отрицательного результата (асинхронный вызов).

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