» Главная
eXcode.ru » Статьи » ОС » Основы операционных систем. Практикум
» Новости
» Опросы
» Файлы
» Журнал



Пользователей: 0
Гостей: 13





строительство фундамента

ritmstroi.ru




Организация файловой системы в UNIX. Работа с файлами и директориями. Понятие о memory mapped файлах




Введение

В материалах нескольких предыдущих семинаров (семинары 1–2, семинар 5) уже затрагивались вопросы работы с файлами в UNIX. Но только теперь, пояснив в лекции понятие файловой системы, мы можем рассмотреть файловую систему UNIX в целом. Наш обзор, правда, ограничится общими вопросами, связанными с организацией файловой системы, и системными вызовами, которые с наибольшей вероятностью могут пригодиться в дальнейшем. Это связано как с ограниченностью времени, которое отводится на работу с файловыми системами в нашем курсе, так и с преимущественно практическим направлением наших занятий.

Разделы носителя информации (partitions) в UNIX

Физические носители информации – магнитные или оптические диски, ленты и т.д., использующиеся как физическая основа для хранения файлов, в операционных системах принято логически делить на разделы (partitions) или логические диски. Причем слово «делить» не следует понимать буквально, в некоторых системах несколько физических дисков могут быть объединены в один раздел. Об этом подробнее рассказывается в лекции 12 в разделе «Общая структура файловой системы».

В операционной системе UNIX физический носитель информации обычно представляет собой один или несколько разделов. В большинстве случаев разбиение на разделы производится линейно, хотя некоторые варианты UNIX могут допускать некое подобие древовидного разбиения (Solaris). Количество разделов и их размеры определяются при форматировании диска. Поскольку форматирование диска относится к области администрирования операционных систем, оно в нашем курсе рассматриваться не будет.

Наличие нескольких разделов на диске может определяться требованиями операционной системы или пожеланиями пользователя. Допустим, пользователь хочет разместить на одном жестком диске несколько операционных систем с возможностью попеременной работы в них, тогда он размещает каждую операционную систему в своем разделе. Или другая ситуация: необходимость работы с несколькими видами файловых систем. Под каждый тип файловой системы выделяется отдельный логический диск. Третий вариант – это разбиение диска на разделы для размещения в разных разделах различных категорий файлов. Скажем, в одном разделе помещаются все системные файлы, а в другом разделе – все пользовательские файлы. Примером операционной системы, внутренние требования которой приводят к появлению нескольких разделов на диске, могут служить ранние версии MS-DOS, для которых максимальный размер логического диска не превышал 32 Мбайт.

Для простоты далее в этих семинарах будем полагать, что у нас имеется только один раздел и, следовательно, одна файловая система. Вопросы взаимного сосуществования нескольких файловых систем в рамках одной операционной системы мы затронем в семинарах 13–14 перед обсуждением реализации подсистемы ввода-вывода.

Логическая структура файловой системы и типы файлов в UNIX

Мы не будем давать здесь определение файла, полагая, что интуитивное представление о файлах у вас имеется, а в лекции 11 (раздел «Введение») было введено понятие о файлах, как об именованных абстрактных объектах, обладающих определенными свойствами. При этом в пространстве имен файлов одному файлу могут соответствовать несколько имен.

В материалах семинаров 1-2 упрощенно говорилось о том, что файлы могут объединяться в директории, и что файлы и директории организованы в древовидную структуру. На нынешнем уровне знаний мы можем сформулировать это более аккуратно. В операционной системе UNIX существуют файлы нескольких типов, а именно:

  • обычные или регулярные файлы;
  • директории или каталоги;
  • файлы типа FIFO или именованные pip′ы;
  • специальные файлы устройств;
  • сокеты (sockets);
  • специальные файлы связи (link).

Что такое регулярные файлы и директории, вам должно быть хорошо известно из личного опыта и из лекций (лекция 11). О способах их отображения в дисковое пространство речь пойдет чуть позже. Файлы типа FIFO были представлены в семинаре 5, когда рассматривалась работа с именованными pip′ами (раздел «Понятие FIFO. Использование системного вызова mknod() для создания FIFO. Функция mkfifo()»). Файлы типа «связь» мы представим в этом семинаре, когда будем обсуждать операции над файлами (раздел «Операции над файлами и директориями») и соответствующие им системные вызовы (раздел «Системные вызовы и команды для выполнения операций над файлами и директориями»). О специальных файлах устройств будет рассказано в материалах семинаров 13–14, посвященных реализации в UNIX подсистемы ввода-вывода и передаче информации с помощью сигналов. Файлы типа «сокет» будут введены в семинарах 15–16, когда мы будем рассматривать вопросы сетевого программирования в UNIX.

Файлы всех перечисленных типов логически объединены в ациклический граф с однонаправленными ребрами, получающийся из дерева в результате сращивания нескольких терминальных узлов дерева или нескольких его нетерминальных узлов таким образом, чтобы полученный граф не содержал циклов. В нетерминальных узлах такого ациклического графа (т.е. в узлах, из которых выходят ребра) могут располагаться только файлы типов «директория» и «связь». Причем из узла, в котором располагается файл типа «связь», может выходить только ровно одно ребро. В терминальных узлах этого ациклического графа (т.е. в узлах, из которых не выходит ребер) могут располагаться файлы любых типов (см. рис. 11–12.1), хотя присутствие в терминальном узле файла типа «связь» обычно говорит о некотором нарушении целостности файловой системы.

Пример графа файловой системы
Рис. 11-12.1. Пример графа файловой системы

В отличие от древовидной структуры набора файлов, где имена файлов связывались с узлами дерева, в таком ациклическом графе имя файла связывается не с узлом, соответствующим файлу, а с входящим в него ребром. Ребра, выходящие из узлов, соответствующих файлам типа «связь», являются неименованными. Надо отметить, что практически во всех существующих реализациях UNIX-подобных систем в узел графа, соответствующий файлу типа «директория», не может входить более одного именованного ребра, хотя стандарт на операционную систему UNIX и не запрещает этого. Правила построения имен ребер (файлов) рассматривались в семинарах 1-2. В качестве полного имени файла может использоваться любое имя, получающееся при прохождении по ребрам от корневого узла графа (т.е. узла, в который не входит ни одно ребро) до узла, соответствующего этому файлу, по любому пути с помощью следующего алгоритма:

  1. Если интересующему нас файлу соответствует корневой узел, то файл имеет имя «/».
  2. Берем первое именованное ребро в пути и записываем его имя, которому предваряем символ «/».
  3. Для каждого очередного именованного ребра в пути приписываем к уже получившейся строке справа символ «/» и имя соответствующего ребра.

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

Организация файла на диске в UNIX на примере файловой системы s5fs. Понятие индексного узла (inode)

Рассмотрим, как организуется на физическом носителе любой файл в UNIX на примере простой файловой системы, впервые появившейся в вариантах операционной системы System V и носящей поэтому название s5fs (System V file system).

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

Для размещения любого файла на диске используется метод индексных узлов (inode – от index node), о котором подробно рассказывается в лекции 12 (раздел «Методы выделения дискового пространства»), и на котором здесь мы останавливаться не будем. Индексный узел содержит атрибуты файла и оставшуюся часть информации о его размещении на диске. Необходимо, однако, отметить, что такие типы файлов, как «связь», «сокет», «устройство», «FIFO» не занимают на диске никакого иного места, кроме индексного узла (им не выделяется логических блоков). Все необходимое для работы с этими типами файлов содержится в их атрибутах.

Перечислим часть атрибутов файлов, хранящихся в индексном узле и свойственных большинству типов файлов. К таким атрибутам относятся:

  • Тип файла и права различных категорий пользователей для доступа к нему.
  • Идентификаторы владельца-пользователя и владельца-группы.
  • Размер файла в байтах (только для регулярных файлов, директорий и файлов типа «связь»).
  • Время последнего доступа к файлу.
  • Время последней модификации файла.
  • Время последней модификации самого индексного узла.

Существует еще один атрибут, о котором мы поговорим в этих семинарах позже, когда мы будем рассматривать операцию связывания файлов в разделе «Системные вызовы и команды для выполнения операций над файлами и директориями» . Количество индексных узлов в разделе является постоянной величиной, определяемой на этапе генерации файловой системы. Все индексные узлы системы организованы в виде массива, хранящегося в заголовке раздела. Каждому файлу соответствует только один элемент этого массива и, наоборот, каждому непустому элементу этого массива соответствует только один файл. Таким образом, каждый файл на диске может быть однозначно идентифицирован номером своего индексного узла (его индексом в массиве).

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

Надо отметить, что свойством уникальности номеров индексных узлов, идентифицирующих файлы, мы уже неявно пользовались при работе с именоваными pip′ами (семинар 5, раздел «Понятие FIFO. Использование системного вызова mknod() для создания FIFO. Функция mkfifo()») и средствами System V IPC (семинары 6–7, раздел «Понятие о System V IPC»). Для именованного pip′a именно номер индексного узла, соответствующего файлу с типом FIFO, является той самой точкой привязки, пользуясь которой, неродственные процессы могут получить данные о расположении pip′а в адресном пространстве ядра и его состоянии и связаться друг с другом. Для средств System V IPC при генерации IPC-ключа с помощью функции ftok() в действительности используется не имя заданного файла, а номер соответствующего ему индексного дескриптора, который по определенному алгоритму объединяется с номером экземпляра средства связи.

Организация директорий (каталогов) в UNIX

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

Основным содержимым файлов типа «директория», если говорить на пользовательском языке, являются имена файлов, лежащих непосредственно в этих директориях, и соответствующие им номера индексных узлов. В терминах представления в виде графа содержимое директорий представляет собой имена ребер, выходящих из узлов, соответствующих директориям, вместе с индексными номерами узлов, к которым они ведут.

В файловой системе s5fs пространство имен файлов (ребер) содержит имена длиной не более 14 символов, а максимальное количество inode в одном разделе файловой системы не может превышать значения 65535. Эти ограничения не позволяют давать файлам осмысленные имена и приводят к необходимости разбиения больших жестких дисков на несколько разделов. Зато они помогают упростить структуру хранения информации в директории. Все содержимое директории представляет собой таблицу, в которой каждый элемент имеет фиксированный размер в 16 байт. Из них 14 байт отводится под имя соответствующего файла (ребра), а 2 байта – под номер его индексного узла. При этом первый элемент таблицы дополнительно содержит ссылку на саму данную директорию под именем «.», а второй элемент таблицы – ссылку на родительский каталог (если он существует), т.е. на узел графа, из которого выходит единственное именованное ребро, ведущее к текущему узлу, под именем «..».

В более современной файловой системе FFS (Fast File System) размерность пространства имен файлов (ребер) увеличена до 255 символов. Это позволило использовать практически любые мыслимые имена для файлов (вряд ли найдется программист, которому будет не лень набирать для имени более 255 символов), но пришлось изменить структуру каталога (чтобы уменьшить его размеры и не хранить пустые байты). В системе FFS каталог представляет собой таблицу из записей переменной длины. В структуру каждой записи входят: номер индексного узла, длина этой записи, длина имени файла и собственно его имя. Две первых записи в каталоге, как и в s5fs, по-прежнему адресуют саму данную директорию и ее родительский каталог.

Понятие суперблока

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

  • Тип файловой системы.
  • Флаги состояния файловой системы.
  • Размер логического блока в байтах (обычно кратен 512 байтам).
  • Размер файловой системы в логических блоках (включая сам суперблок и массив inode).
  • Размер массива индексных узлов (т.е. сколько файлов может быть размещено в файловой системе).
  • Число свободных индексных узлов (сколько файлов еще можно создать).
  • Число свободных блоков для размещения данных.
  • Часть списка свободных индексных узлов.
  • Часть списка свободных блоков для размещения данных.

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

Операции над файлами и директориями

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

В лекции (лекция 11, раздел «Организация файлов и доступ к ним») речь шла о том, что существует два основных вида файлов, различающихся по методу доступа: файлы последовательного доступа и файлы прямого доступа. Если рассматривать файлы прямого и последовательного доступа как абстрактные типы данных, то они представляются как нечто, содержащее информацию, над которой можно совершать следующие операции:

  • Для последовательного доступа: чтение очередной порции данных (read), запись очередной порции данных (write) и позиционирование на начале файла (rewind).
  • Для прямого доступа: чтение очередной порции данных (read), запись очередной порции данных (write) и позиционирование на требуемой части данных (seek).

Работа с объектами этих абстрактных типов подразумевает наличие еще двух необходимых операций: создание нового объекта (new) и уничтожение существующего объекта (free).

Расширение математической модели файла за счет добавления к хранимой информации атрибутов, присущих файлу (права доступа, учетные данные), влечет за собой появление еще двух операций: прочитать атрибуты (get attribute) и установить их значения (set attribute).

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

Для директории, например, такой набор операций, определяемый ее внутренним строением, может выглядеть так: операции new, free, set attribute и get attribute остаются без изменений, а операции read, write и rewind (seek) заменяются более высокоуровневыми:

  • прочитать запись, соответствующую имени файла, – get record;
  • добавить новую запись – add record;
  • удалить запись, соответствующую имени файла, – delete record.

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

  • Операции для работы с атрибутами файловget attribute, set attribute.
  • Операции для работы с содержимым файлов – read, write, rewind(seek) для регулярных файлов и get record, add record, delete record для директорий.
  • Операция создания регулярного файла в некоторой директории (создание нового узла графа и добавление в граф нового именованного ребра, ведущего в этот узел из некоторого узла, соответствующего директории) – create. Эту операцию можно рассматривать как суперпозицию двух операций: базовой операции new для регулярного файла и add record для соответствующей директории.
  • Операция создания поддиректории в некоторой директорииmake directory. Эта операция отличается от предыдущей операции create занесением в файл новой директории информации о файлах с именами «.» и «..», т.е. по сути дела она есть суперпозиция операции create и двух операций add record.
  • Операция создания файла типа «связь»symbolic link.
  • Операция создания файла типа «FIFO»make FIFO.
  • Операция добавления к графу нового именованного ребра, ведущего от узла, соответствующего директории, к узлу, соответствующему любому другому типу файла, – link. Это просто add record с некоторыми ограничениями.
  • Операция удаления файла, не являющегося директорией или «связью» (удаление именованного ребра из графа, ведущего к терминальной вершине с одновременным удалением этой вершины, если к ней не ведут другие именованные ребра), – unlink.
  • Операция удаления файла типа «связь» (удаление именованного ребра, ведущего к узлу, соответствующему файлу типа «связь», с одновременным удалением этого узла и выходящего из него неименованного ребра, если к этому узлу не ведут другие именованные ребра), – unlink link.
  • Операция рекурсивного удаления директории со всеми входящими в нее файлами и поддиректориями – remove directory.
  • Операция переименования файла (ребра графа) – rename.
  • Операция перемещения файла из одной директории в другую (перемещается точка выхода именованного ребра, которое ведет к узлу, соответствующему данному файлу) – move.

Возможны и другие подобные операции.

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

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

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

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

В семинарах 1-2 рассматривался ряд команд, позволяющих изменять атрибуты файлаchmod, chown, chgrp, команду копирования файлов и директорийcp, команду удаления файлов и директорийrm, команду переименования и перемещения файлов и директорийmv, команду просмотра содержимого директорийls.

В материалах семинара 5, посвященного потокам ввода-вывода, рассказывалось о хранении информации о файлах внутри адресного пространства процесса с помощью таблицы открытых файлов, о понятии файлового дескриптора, о необходимости введения операций открытия и закрытия файлов (системные вызовы open() и close()) и об операциях чтения и записи (системные вызовы read() и write()). Мы обещали вернуться к более подробному рассмотрению затронутых вопросов в текущих семинарах. Пора выполнять обещанное. Далее в этом разделе, если не будет оговорено особо, под словом «файл» будет подразумеваться регулярный файл.

Вся информация об атрибутах файла и его расположении на физическом носителе содержится в соответствующем файлу индексном узле и, возможно, в нескольких связанных с ним логических блоках. Для того чтобы при каждой операции над файлом не считывать эту информацию с физического носителя заново, представляется логичным, считав информацию один раз при первом обращении к файлу, хранить ее в адресном пространстве процесса или в части адресного пространства ядра, характеризующей данный процесс. Именно поэтому в лекции 2 данные о файлах, используемых процессом, были отнесены к составу системного контекста процесса, содержащегося в его PCB.

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

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

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

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

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

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

Взаимосвязи между таблицами, содержащими  данные об открытых файлах в системе
Рис. 11-12.2. Взаимосвязи между таблицами, содержащими данные об открытых файлах в системе

Естественно, что для хранения этой информации применяются три различные связанные структуры данных, лежащие, как правило, в адресном пространстве ядра операционной системы, – таблица открытых файлов процесса, системная таблица открытых файлов и таблица индексных узлов открытых файлов. Для доступа к этой информации в управляющем блоке процесса заводится таблица открытых файлов, каждый непустой элемент которой содержит ссылку на соответствующий элемент системной таблицы открытых файлов, содержащей данные, необходимые для совместного использования файла близко родственными процессами. Из системной таблицы открытых файлов мы, в свою очередь, можем по ссылке добраться до общих данных о файле, содержащихся в таблице индексных узлов открытых файлов (см. рис. 11–12.2). Только таблица открытых файлов процесса входит в состав его PCB и, соответственно, наследуется при рождении нового процесса. Индекс элемента в этой таблице (небольшое целое неотрицательное число) или файловый дескриптор является той величиной, характеризующей файл, которой может оперировать процесс при работе на уровне пользователя. В эту же таблицу открытых файлов помещаются и ссылки на данные, описывающие другие потоки ввода-вывода, такие как pipe и FIFO (об этом уже упоминалось в семинаре 5). Как мы увидим позже (в материалах семинаров 15–16, посвященных сетевому программированию), эта же таблица будет использоваться и для размещения ссылок на структуры данных, необходимых для передачи информации от процесса к процессу по сети.

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

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

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

Поведение таблицы открытых файлов процесса и связанных с ней таблиц при системных вызовах exit(), exec() и fork() рассматривалось в материалах семинара 5.

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

Системный вызов creat()

Прототип системного вызова

#include <fcntl.h>

int creat(char *path, int mode); 

Описание системного вызова

Системный вызов creat эквивалентен системному вызову open() с параметром flags, установленным в значение O_CREAT | O_WRONLY | O_TRUNC.

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

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

Параметр mode устанавливает атрибуты прав доступа различных категорий пользователей к новому файлу при его создании. Этот параметр задается как сумма следующих восьмеричных значений:

  • 0400 – разрешено чтение для пользователя, создавшего файл.
  • 0200 – разрешена запись для пользователя, создавшего файл.
  • 0100 – разрешено исполнение для пользователя, создавшего файл.
  • 0040 – разрешено чтение для группы пользователя, создавшего файл.
  • 0020 – разрешена запись для группы пользователя, создавшего файл.
  • 0010 – разрешено исполнение для группы пользователя, создавшего файл.
  • 0004 – разрешено чтение для всех остальных пользователей
  • 0002 – разрешена запись для всех остальных пользователей
  • 0001 – разрешено исполнение для всех остальных пользователей

При создании файла реально устанавливаемые права доступа получаются из стандартной комбинации параметра mode и маски создания файлов текущего процесса umask, а именно – они равны mode & ~umask.

Возвращаемое значение

Системный вызов возвращает значение файлового дескриптора для открытого файла при нормальном завершении и значение -1 при возникновении ошибки.

Операция чтения атрибутов файла. Системные вызовы stat(), fstat() и lstat(). Для чтения всех атрибутов файла в специальную структуру могут применяться системные вызовы stat(), fstat() и lstat(). Разъяснение понятий жесткой и мягкой (символической) связи, встречающихся в описании системных вызовов, будет дано позже при рассмотрении операций связывания файлов.

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

Прототипы системных вызовов

#include <sys/stat.h>
#include <unistd.h>
int stat(char *filename, 
         struct stat *buf);
int fstat(int fd, struct stat *buf);
int lstat(char *filename, 
          struct stat *buf);

Описание системных вызовов

Настоящее описание не является полным описанием этих системных вызовов, а приспособлено для целей данного курса. Для получения полного описания обращайтесь в UNIX Manual.

Системные вызовы stat, fstat и lstat служат для получения информации об атрибутах файла.

Системный вызов stat читает информацию об атрибутах файла, на имя которого указывает параметр filename, и заполняет ими структуру, расположенную по адресу buf. Заметим, что имя файла должно быть полным, либо должно строиться относительно той директории, которая является текущей для процесса, совершившего вызов. Если имя файла относится к файлу типа «связь», то читается информация (рекурсивно!) об атрибутах файла, на который указывает символическая связь.

Системный вызов lstat идентичен системному вызову stat за одним исключением: если имя файла относится к файлу типа «связь», то читается информация о самом файле типа «связь».

Системный вызов fstat идентичен системному вызову stat, только файл задается не именем, а своим файловым дескриптором (естественно, файл к этому моменту должен быть открыт).

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

Структура stat в различных версиях UNIX может быть описана по-разному. В Linux она содержит следующие поля:

struct stat {
dev_t st_dev; 
  /* устройство, на котором 
     расположен файл */
ino_t st_ino; 
  /* номер индексного узла для файла */ 
mode_t st_mode; 
  /* тип файла и права доступа к нему */
nlink_t st_nlink; 
  /* счетчик числа жестких связей */
uid_t st_uid; 
  /* идентификатор пользователя владельца */
gid_t st_gid; 
  /* идентификатор группы владельца */
dev_t st_rdev;
  /*тип устройства для специальных файлов 
    устройств*/
off_t st_size; 
  /* размер файла в байтах (если определен 
     для данного типа файлов) */
unsigbed long st_blksize; 
  /* размер блока для файловой системы */
unsigned long st_blocks; 
  /* число выделенных блоков */
time_t st_atime; 
  /* время последнего доступа к файлу */
time_t st_mtime; 
  /* время последней модификации файла */
time_t st_ctime; 
  /* время создания файла */
}

Для определения типа файла можно использовать следующие логические макросы, применяя их к значению поля st_mode:

  • S_ISLNK(m) – файл типа «связь»?
  • S_ISREG(m) – регулярный файл?
  • S_ISDIR(m) – директория?
  • S_ISCHR(m) – специальный файл символьного устройства?
  • S_ISBLK(m) – специальный файл блочного устройства?
  • S_ISFIFO(m) – файл типа FIFO?
  • S_ISSOCK(m) – файл типа «socket»?

Младшие 9 бит поля st_mode определяют права доступа к файлу подобно тому, как это делается в маске создания файлов текущего процесса.

Возвращаемое значение

Системные вызовы возвращают значение 0 при нормальном завершении и значение -1 при возникновении ошибки.

Операции изменения атрибутов файла. Большинство операций изменения атрибутов файла обычно выполняется пользователем в интерактивном режиме с помощью команд операционной системы. О них уже шла речь в материалах семинаров 1–2, и мы не будем возвращаться к ним вновь. Отметим только операцию изменения размеров файла, а точнее операцию его обрезания, без изменения всех других атрибутов, кроме, быть может, времени последнего доступа к файлу и его последней модификации. Для того чтобы уменьшить размеры существующего файла до 0, не затрагивая остальных его характеристик (прав доступа, даты создания, учетной информации и т.д.), можно при открытии файла использовать в комбинации флагов системного вызова open() флаг O_TRUNC. Для изменения размеров файла до любой желаемой величины (даже для его увеличения во многих вариантах UNIX, хотя изначально этого не предусматривалось!) может использоваться системный вызов ftruncate(). При этом, если размер файла мы уменьшаем, то вся информация в конце файла, не влезающая в новый размер, будет потеряна. Если же размер файла мы увеличиваем, то это будет выглядеть так, как будто мы дополнили его до недостающего размера нулевыми байтами.

Системный вызов ftruncate()

Прототип системного вызова

#include <sys/types.h>
#include <unistd.h>
int ftruncate(int fd, size_t length);

Описание системного вызова

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

Параметр fd является дескриптором соответствующего файла, т. е. значением, которое вернул системный вызов open().

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

Возвращаемое значение

Системный вызов возвращает значение 0 при нормальном завершении и значение -1 при возникновении ошибки.

Операции чтения из файла и записи в файл. Для операций чтения из файла и записи в файл применяются системные вызовы read() и write(), которые мы уже обсуждали ранее (семинар 5, раздел «Системные вызовы read(), write(), close()»).

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

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

Операция изменения указателя текущей позиции. Системный вызов lseek(). С точки зрения процесса все регулярные файлы являются файлами прямого доступа. В любой момент процесс может изменить положение указателя текущей позиции в открытом файле с помощью системного вызова lseek().

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

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

Системный вызов lseek()

Прототип системного вызова

#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, 
            int whence);

Описание системного вызова

Системный вызов lseek предназначен для изменения положения указателя текущей позиции в открытом регулярном файле.

Параметр fd является дескриптором соответствующего файла, т. е. значением, которое вернул системный вызов open().

Параметр offset совместно с параметром whence определяют новое положение указателя текущей позиции следующим образом:

  • Если значение параметра whence равно SEEK_SET, то новое значение указателя будет составлять offset байт от начала файла. Естественно, что значение offset в этом случае должно быть не отрицательным.
  • значение параметра whence равно SEEK_CUR, то новое значение указателя будет составлять старое значение указателя + offset байт. При этом новое значение указателя не должно стать отрицательным.
  • Если значение параметра whence равно SEEK_END, то новое значение указателя будет составлять длина файла + offset байт. При этом новое значение указателя не должно стать отрицательным.

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

Тип данных off_t обычно является синонимом типа long.

Возвращаемое значение

Системный вызов возвращает новое положение указателя текущей позиции в байтах от начала файла при нормальном завершении и значение -1 при возникновении ошибки.

Операция добавления информации в файл. Флаг O_APPEND. Хотя эта операция по сути дела является комбинацией двух уже рассмотренных операций, мы считаем нужным упомянуть ее особо. Если открытие файла системным вызовом open() производилось с установленным флагом O_APPEND, то любая операция записи в файл будет всегда добавлять новые данные в конец файла, независимо от предыдущего положения указателя текущей позиции (как если бы непосредственно перед записью был выполнен вызов lseek() для установки указателя на конец файла).

Операции создания связей. Команда ln, системные вызовы link() и symlink(). С операциями, позволяющими изменять логическую структуру файловой системы, такими как создание файла, мы уже сталкивались в этом разделе. Однако операции создания связи служат для проведения новых именованных ребер в уже существующей структуре без добавления новых узлов или для опосредованного проведения именованного ребра к уже существующему узлу через файл типа «связь» и неименованное ребро. Такие операции мы до сих пор не рассматривали, поэтому давайте остановимся на них подробнее.

Допустим, что несколько программистов совместно ведут работу над одним и тем же проектом. Файлы, относящиеся к этому проекту, вполне естественно могут быть выделены в отдельную директорию так, чтобы не смешиваться с файлами других пользователей и другими файлами программистов, участвующих в проекте. Для удобства каждый из разработчиков, конечно, хотел бы, чтобы эти файлы находились в его собственной директории. Этого можно было бы добиться, копируя по мере изменения новые версии соответствующих файлов из директории одного исполнителя в директорию другого исполнителя. Однако тогда, во-первых, возникнет ненужное дублирование информации на диске. Во-вторых, появится необходимость решения тяжелой задачи: синхронизации обновления замены всех копий этих файлов новыми версиями.

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

В операционной системе UNIX связь может быть создана двумя различными способами.

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

Использование жестких связей приводит к возникновению двух проблем.

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

Вторая проблема связана с опасностью превращения логической структуры файловой системы из ациклического графа в циклический и с возможной неопределенностью толкования записи с именем «..» в содержимом директорий. Для их предотвращения во всех существующих вариантах операционной системы UNIX запрещено создание жестких связей, ведущих к уже существующим директориям (несмотря на то, что POSIX-стандарт для операционной системы UNIX разрешает подобную операцию для пользователя root). Поэтому мы и говорили о том, что в узел, соответствующий файлу типа «директория», не может вести более одного именованного ребра. (В операционной системе Linux по непонятной причине дополнительно запрещено создание жестких связей, ведущих к специальным файлам устройств.)

Команда ln

Синтаксис команды

ln [options] source [dest]
ln [options] source ... directory

Описание команды

Настоящее описание не является полным описанием команды ln, а описывает только ее опции, используемые в данном курсе. Для получения полного описания обращайтесь к UNIX Manual

Команда ln предназначена для реализации операции создания связи в файловой системе. В нашем курсе мы будем использовать две формы этой команды.

Первая форма команды, когда в качестве параметра source задается имя только одного файла, а параметр dest отсутствует, или когда в качестве параметра dest задается имя файла, не существующего в файловой системе, создает связь к файлу, указанному в качестве параметра source, в текущей директории с его именем (если параметр dest отсутствует) или с именем dest (полным или относительным) в случае наличия параметра dest.

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

Команда ln без опций служит для создания жестких связей (hard link), а команда ln с опцией –s – для создания мягких (soft link) или символических (symbolic) связей.

Примечание: во всех существующих версиях UNIX (несмотря на стандарт POSIX) запрещено создание жестких связей к директориям. Операционная система Linux запрещает также, по непонятным причинам, создание жестких связей к специальным файлам устройств.

Для создания жестких связей применяются команда операционной системы ln без опций и системный вызов link().

Надо отметить, что системный вызов К началу статьи





Добавил: MadvEXДата публикации: 2006-02-20 19:22:28
Рейтинг статьи:3.00 [Голосов 5]Кол-во просмотров: 6709

Комментарии читателей

Всего комментариев: 4

2012-04-30 02:58:48
Talopiciowl
<a href=http://tutdlenet.ru/>
lineage dle шаблоны</a>

2012-04-22 15:48:44
cyncscienia
<a href=http://silven.ru/khkhkh-onlajjn/>
лучшее порно смотреть онлайн бесплатно</a>

2011-06-30 09:58:54
Hienetelmrere
Собираем базы клиентов по интернет
Тел +79133913837
ICQ: 6288862
Skype: prodawez3837
Email: prodawez@mixmail.com

2011-05-08 19:31:50
Hienetelmrere
Соберем для Вас по сети интернет базу данных
потенциальных клиентов для Вашего Бизнеса
(название, телефон, факс, email, сайт, имена и др информацию)
Много! Быстро! Точно!
Узнайте более подробную информацию по:
Телефон +79133913837
ICQ: 6288862
Skype: prodawez3837
Email: prodawez@mixmail.com
Ваше имя: *
Текст записи: *
Имя:

Пароль:



Регистрация

Что для вас важнее в ПО
Его размер
7% (9)
Его цена
7% (8)
Его простота и доступность
17% (20)
Его функциональность
64% (77)
Наличие дополнительных фич
0% (0)
Наличие русского языка
5% (6)
Графицеский интерфейс
1% (1)
Изготовитель
0% (0)

Проголосовало: 121
Если ты думаешь, что знаешь язык программирования, то ошибаешься - твои знания безнадежно устарели.
Рейтинг: 5.5/10 (2)
Посмотреть все анекдоты

 
eXcode.ru » Статьи » ОС » Основы операционных систем. Практикум