Файл: Debian Таненбаум Бос.pdf

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

Категория: Книга

Дисциплина: Операционные системы

Добавлен: 29.10.2018

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

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

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

4.1. Файлы   

311

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

 

 Create (Создать). Создает файл без данных. Цель вызова состоит в объявлении 

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

 

 Delete (Удалить). Когда файл больше не нужен, его нужно удалить, чтобы освобо-

дить дисковое пространство. Именно для этого и предназначен этот системный 
вызов.

 

 Open (Открыть). Перед использованием файла процесс должен его открыть. Цель 

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

 

 Close (Закрыть). После завершения всех обращений к файлу потребность в его 

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

 

 Read (Произвести чтение). Считывание данных из файла. Как правило, байты по-

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

 

 Write (Произвести запись). Запись данных в файл, как правило, с текущей позиции. 

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

 

 Append (Добавить). Этот вызов является усеченной формой системного вызова 

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

 

 Seek (Найти). При работе с файлами произвольного доступа нужен способ указания 

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

 

 Get attributes (Получить атрибуты). Процессу для работы зачастую необходимо 

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

 

 Set attributes (Установить атрибуты). Значения некоторых атрибутов могут уста-

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


background image

312  

 Глава 4. Файловые системы 

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

 

 Rename (Переименовать). Нередко пользователю требуется изменить имя существу-

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

4.1.7. Пример программы, использующей 
файловые системные вызовы

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

Листинг 4.1. Простая программа копирования файла

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

#include <sys/types.h>             /* включение необходимых заголовочных файлов 
*/
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv[]);  /* ANSI-прототип */

#define BUF_SIZE 4096              /* используется буфер размером 
                                   4096 байт */
#define OUTPUT_MODE 0700           /* биты защиты для выходного файла */
int main(int argc, char *argv[])
{
    int in_fd, out_fd, rd_count, wt_count;
    char buffer[BUF_SIZE];

    if (argc != 3) exit(1);        /* если argc не равен 3, возникает
                                   синтаксическая ошибка */

    /* Открытие входного и создание выходного файла */
    in_fd = open(argv[1], O_RDONLY);        /* открытие исходного файла */
    if (in_fd < 0) exit(2);                 /* если он не открывается, выйти */
    out_fd = creat(argv[2], OUTPUT_MODE);   /* создание файла-приемника */
    if (out_fd < 0) exit(3);                /* если он не создается, выйти */

    /* Цикл копирования */
    while (TRUE) {
        rd_count = read(in_fd, buffer, BUF_SIZE); /* чтение блока данных */
    if (rd_count <= 0) break;      /* в конце файла или при ошибке – выйти из 
                                   цикла */
        wt_count = write(out_fd, buffer, rd_count); /* запись данных */


background image

4.1. Файлы   

313

        if (wt_count <= 0) exit(4); /* при wt_count <= 0 возникает ошибка */
    }

    /* Закрытие файлов */
    close(in_fd);
    close(out_fd);
    if (rd_count == 0)            /* при последнем чтении ошибки не возникло */
        exit(0);
    else
        exit(5);                  /* ошибка при последнем чтении */
}

Эта программа с именем copyfile может быть вызвана, к примеру, из командной строки

copyfile abc xyz

чтобы скопировать файл 

abc

 в файл 

xyz

. Если файл 

xyz

 уже существует, то он будет 

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

Благодаря четырем операторам #include в самом начале программы в нее включается 
большое количество определений и прототипов функций. Это нужно для совмести-
мости программы с соответствующими международными стандартами, но больше это 
нас интересовать не будет. Следующая строка, согласно требованию стандарта ANSI C, 
содержит прототип функции main, но сейчас это нас тоже не интересует.

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

У основной программы, которая называется main, имеется два аргумента — argc 
и argv. Значения этим аргументам присваиваются операционной системой при вы-
зове программы. В первом аргументе указывается количество строковых значений, 
присутствующих в командной строке, вызывающей программу, включая имя самой 
программы. Его значение должно быть равно 3. Второй аргумент представляет собой 
массив указателей на аргументы командной строки. В примере, приведенном ранее, 
элементы этого массива будут содержать указатели на следующие значения:

argv[0] = "copyfi le"
argv[1] = "abc"
argv[2] = "xyz"

Через этот массив программа получает доступ к своим аргументам.

В программе объявляются пять переменных. В первых двух переменных, in_ fd 
и out_ fd, будут храниться дескрипторы файлов — небольшие целые числа, возвраща-
емые при открытии файла. Следующие две переменные, rd_count и wt_count, являются 
байтовыми счетчиками, возвращаемыми процедурами read и write соответственно. По-
следняя переменная, buffer, используется для хранения считанных и предоставления 
записываемых данных.


background image

314  

 Глава 4. Файловые системы 

Первый исполняемый оператор проверяет, не равно ли значение счетчика аргументов 
argc 3. Если счетчик argc не равен 3, программа завершается с кодом 1. Любой код за-
вершения программы отличный от 0 означает, что произошла ошибка. Единственный 
применяемый в этой программе способ сообщения об ошибках — это код завершения 
программы. Окончательный вариант этой программы выводил бы сообщения об 
ошибках.

Затем программа пытается открыть входной файл и создать выходной файл. Если от-
крытие файла проходит успешно, операционная система присваивает переменной in_ fd 
небольшое целочисленное значение, чтобы идентифицировать файл. Это целое число 
может включаться в последующие вызовы, чтобы система знала, какой файл им нужен. 
Аналогично этому, если успешно создается выходной файл, переменной out_ fd также 
присваивается идентифицирующее его значение. Второй аргумент процедуры creat уста-
навливает код защиты создаваемого файла. Если не удается открыть файл или создать 
его, то значение соответствующего дескриптора файла устанавливается в –1 и проис-
ходит выход из программы с соответствующим кодом ошибки.

Затем наступает черед цикла копирования, который начинается с попытки считать 
в буфер buffer 4 Кбайт данных. Это делается путем вызова библиотечной процедуры 
read, которая на самом деле осуществляет системный вызов read. Первый параметр 
идентифицирует файл, второй указывает буфер, а третий сообщает, сколько байтов 
нужно считать. Значение, присвоенное rd_count, дает количество реально считанных 
байтов. Обычно это значение равно 4096, за исключением того случая, когда в файле 
останется меньше байтов. При достижении конца файла это значение будет равно 0. 
Как только значение rd_count станет нулевым или отрицательным, копирование не 
сможет продолжаться, поэтому для прекращения цикла (который в противном случае 
был бы бесконечным) выполняется оператор break.

Обращение к процедуре write приводит к выводу содержимого буфера в выходной 
файл. Первый параметр идентифицирует файл, второй указывает буфер, а третий, 
аналогично параметрам процедуры read, сообщает, сколько байтов нужно записать. 
Следует заметить, что счетчик байтов содержит количество реально считанных байтов, 
а не значение BUF_SIZE. Это важно, поскольку при последнем считывании не будет 
возвращено число 4096, если только длина файла не окажется кратной 4 Кбайт.

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

Несмотря на то что системные вызовы Windows отличаются от системных вызовов 
UNIX, общая структура программы Windows, предназначенной для копирования 
файлов и запускаемой из командной строки, примерно такая же, как у программы, по-
казанной в листинге 4.1. Системные вызовы Windows 8 будут рассмотрены в главе 11.

4.2. Каталоги

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


background image

4.2. Каталоги   

315

4.2.1. Системы с одноуровневыми каталогами

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

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

Рис. 4.3. Система с одноуровневым каталогом, 

содержащим четыре файла

4.2.2. Иерархические системы каталогов

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

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

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

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