Слайд 1ОП.14
Основы функционирования UNIX - систем
занятие 04, 05
Слайд 2Файловая система
Функции API для работы с файлами
API, программный интерфейс приложения,
интерфейс прикладного программирования (от английского application programming interface, API) —
описание способов, которыми одна компьютерная программа может взаимодействовать с другой программой.
Обычно входит в описание стандарта вызовов функций операционной системы.
Часто реализуется отдельной программной библиотекой или сервисом операционной системы.
Используется программистами при написании всевозможных приложений.
Слайд 3Функции API для работы с файлами
В операционные системы UNIX включен
целый ряд функций прикладного интерфейса программирования API для работы с
объектами файловой системы или, по-другому, системных вызовов для выполнения файловых операций.
При использовании функций API мы будем применять только те функции, которые являются общими для всех реализаций UNIX, т. е. придерживаться общих спецификаций, изложенных в стандарте POSIX.
Слайд 4Функции API для работы с файлами
Стандарт POSIX предлагает стандартный интерфейс
прикладного программирования операционных систем, в котором определены функции API для
манипулирования файлами и процессами.
Например, системный вызов fork (), создающий новый процесс и рассмотренный нами ранее, также включен в этот стандарт.
Если особо не оговорено, все дальнейшие рассуждения будут проводиться применительно к спецификациям POSIX.
Слайд 5Функции API для работы с файлами
Системные вызовы для работы
с объектами файловой системы сгруппированы в таблице.
Слайд 6Функции API для работы с файлами
Системные вызовы. Таблица (продолжение).
Слайд 7Функции API для работы с файлами
Системные вызовы. Таблица (продолжение).
Слайд 8Функции API для работы с файлами
Системные вызовы. Таблица (окончание).
Слайд 9Функции API для работы с файлами
В большинстве команд операционной системы
UNIX использованы эти функции, причем иногда мнемонические обозначения совпадают.
Например, на основе функций API link () и unlink () созданы одноименные команды UNIX. To же самое касается, например, команд chmod и chown, в основе которых лежат одноименные функции API.
Вкратце рассмотрим синтаксис наиболее часто используемых функций API: open(), read(), write() и close().
Начнем с системного вызова open().
Слайд 10Функции API для работы с файлами
Функция open() устанавливает соединение между
процессом и файлом, позволяя как создавать новые, так и открывать
существующие файлы для выполнения операций чтения и/или записи.
Открытие файла означает получение дескриптора (описателя) на данный файл, который будет в дальнейшем использоваться во всех операциях с файлом. Дескриптор (handle) представляет собой беззнаковое целое число и используется операционной системой для доступа к файлу.
Нельзя работать с данными файла, не открыв его и не получив дескриптор.
Слайд 11Функции API для работы с файлами
Очень часто при определении операции
записи или чтения говорят "запись в дескриптор файла" или "чтение
из дескриптора файла", имея в виду открытый файл, для которого получен дескриптор.
По завершению всех операций с файлом дескриптор следует закрыть (в принципе, операционная система по завершению процесса закрывает все открытые дескрипторы, принадлежащие ему, но это плохая практика — оставлять открытыми дескрипторы файлов).
Слайд 12Функции API для работы с файлами
Функция имеет прототип:
#include
#include int
open(const char* path,
int access_mode, mode_t permission);
Слайд 13Функции API для работы с файлами
Смысл параметров функции следующий:
•
path — имя файла, которое
может быть:
- абсолютным (символьная строка, начинающаяся символом / ),
- относительным (символьная строка, не начинающаяся символом / ),
- символической ссылкой;
Слайд 14Функции API для работы с файлами
• access_mode —
целочисленное значение, показывающее, какие типы доступа
к файлу разрешены вызывающему процессу.
Все типы доступа определены как макросы в файле fcntl.h и могут иметь одно из следующих значений:
- O_RDONLY — файл доступен только для чтения;
- O_WRONLY — файл доступен только для записи;
- O_RDWR — файл доступен для чтения и для записи.
Слайд 15Функции API для работы с файлами
Кроме того, можно
задавать один или несколько указанных далее модификаторов, логически складывая
их с указанными флагами доступа, что расширяет или изменяет механизм доступа:
- O_APPEND — позволяет добавить данные в конец файла;
- O_CREAT — позволяет создать файл, если он не существует;
- O_TRUNC — отбрасывает содержимое файла, устанавливая его размер равным 0;
Слайд 16Функции API для работы с файлами
• permission —
необходим только в том случае,
если в параметре access_mode присутствует флаг O_CREAT.
Этот параметр задает права доступа к файлу для владельца, группы-владельца и остальных пользователей.
Пример вызова функции open() приведен далее:
int fd = open("/home/userl/text", 0_RDWR | 0_CREAT | 0_APPEND);
Слайд 17Функции API для работы с файлами
Здесь функция open() открывает файл
/home/userl/text для чтения/записи, при этом, если файл не существует, он
создается, а для существующего файла данные будут добавлены в его конец.
Функция возвращает дескриптор fd, который помещается в таблицу дескрипторов файлов процесса и будет использоваться при последующих операциях.
Слайд 18Функции API для работы с файлами
Функция read() читает
блок данных указанного размера из
файла с заданным дескриптором.
Прототип функции выглядит так:
#include
#include
ssize_t read(int fd, void* buf, size_t size);
Слайд 19Функции API для работы с файлами
Параметры функции означают следующее:
•
fd — дескриптор открытого файла, полученный ранее с помощью
системного вызова open() или иным способом;
• buf — буфер данных, куда помещаются данные, считанные из файла;
• size — количество байтов, которое необходимо прочитать из файла (тип этого параметра эквивалентен unsigned int).
Функция read() возвращает количество байтов, прочитанных в буфер памяти buf.
Слайд 20Функции API для работы с файлами
Вот пример использования функции:
char
buf[128];
int bytesRead;
bytesRead = read(fd, buf, sizeof(buf));
В этом
примере функция read() пытается прочитать 128 байтов в буфер buf с открытого файла, указанного дескриптором fd.
В переменную bytesRead будет помещено количество считанных байтов.
Слайд 21Функции API для работы с файлами
Функция write() записывает блок данных
фиксированного размера в файл, дескриптор которого задается в качестве первого
параметра функции.
Прототип функции выглядит так:
#include
#include
ssize_t write(int fd, const void* buf, size_t size);
Слайд 22Функции API для работы с файлами
Параметры функции имеют следующий смысл:
• fd — дескриптор открытого файла, полученный при вызове
функции open() или иным способом;
• buf — буфер данных, откуда выбираются данные;
• size — количество записываемых байтов.
Слайд 23Функции API для работы с файлами
Следующий пример
показывает использование функции
write():
char *str = "String to write";
write(fd, str, strlen(str));
Слайд 24Функции API для работы с файлами
Здесь в открытый файл с
дескриптором fd записывается строка str, размер которой определяется функцией strien(str).
Функция
close() закрывает открытый дескриптор файла, принимая значение дескриптора в качестве единственного параметра.
Слайд 25Операции с файлами. Индексные дескрипторы
Любой файл операционной системы UNIX
описывается информационным блоком, который называется i-node (индексный дескриптор).
Это исключительно
важная структура, поскольку повреждение или некорректная информация, помещенная в индексный дескриптор, фактически означает уничтожение файла.
Все операции с объектами файловой системы осуществляются только через индексные дескрипторы, поэтому рассмотрим эту информационную структуру подробно.
Слайд 26Операции с файлами. Индексные дескрипторы
Если файл открыт, то операционная
система создает копию индексного дескриптора в памяти, в то время
как исходный дескриптор хранится на диске.
В индексном дескрипторе находится вся информация о файле, за исключением его имени, которое хранится в каталоге, где размещен файл.
Индексный дескриптор содержит следующие характеристики файла:
• атрибуты доступа и тип файла;
• информацию о владельце файла;
Слайд 27Операции с файлами. Индексные дескрипторы
• время последнего изменения,
последнего доступа и последней модификации;
• счетчик жестких
ссылок;
• размер файла в байтах;
• адреса физических блоков на жестком диске.
Структура информационных полей в индексном дескрипторе одинакова для всех операционных систем UNIX и состоит из 16-ти 32-разрядных значений (смотри следующий рисунок).
Слайд 29Операции с файлами. Индексные дескрипторы
Индексный дескриптор содержит 13
адресов физических блоков, каждый из которых
занимает 3 байта (в последних версиях FreeBSD и Linux операционной системы UNIX для адресации используется 4 байта).
Первые 10 адресов блоков непосредственно ссылаются на блоки данных, а оставшиеся 3 содержат адреса индексных блоков, которые, в свою очередь, ссылаются на следующие блоки данных.
Предполагается, что все данные файла, размещенные по физическим адресам, указанным в индексном дескрипторе, находятся на одном и том же физическом диске.
Слайд 30Операции с файлами. Индексные дескрипторы
Если файл
открывается для выполнения операции,
ядро помещает в память копию индексного дескриптора из таблицы индексных дескрипторов файлов.
Слайд 31Операции с файлами. Индексные дескрипторы
Такая копия "в
памяти" помимо стандартной информации,
рассмотренной ранее, содержит несколько дополнительных полей:
• счетчик ссылок (reference count), показывающий количество одновременно открытых копий данного файла;
• статусную информацию, указывающую на такие состояния:
• индексный дескриптор блокирован;
• процесс ожидает разблокирования;
Слайд 32Операции с файлами. Индексные дескрипторы
•
индексный дескриптор, находящийся в памяти, отличается от версии на диске
(dirty);
• выполнены какие-то модификации файла, не сохраненные к настоящему моменту на диске;
• номер дискового устройства, где расположен файл.
Обратите внимание на то, что в индексном дескрипторе не указывается имя файла — операционная система помещает имя файла вместе с номером индексного дескриптора (i-number) в каталог, где располагается файл.
Слайд 33Операции с файлами. Индексные дескрипторы
Для более ранних
версий операционных систем UNIX каждая
запись каталога состоит из 16 байтов, первые два из которых указывают на номер индексного дескриптора, а остальные 14 содержат имя файла.
Поскольку имя файла было ограничено 14-ю символами, в современных версиях UNIX запись в каталоге имеет следующий формат (смотри следующий рисунок).
Слайд 34Операции с файлами. Индексные дескрипторы
Структура записи в каталоге
Слайд 35Операции с файлами. Индексные дескрипторы
В операционной системе UNIX
для каждой файловой системы создается таблица индексных дескрипторов,
в которой хранится информация обо всех файлах.
Каждая запись в такой таблице содержит индексный дескриптор и его номер.
Например, если ядру понадобится получить доступ к информации о файле, индексный дескриптор которого имеет номер 69, то оно будет просматривать все записи таблицы индексных дескрипторов в поисках записи, содержащей индексный дескриптор с номером 69.
Слайд 36Операции с файлами. Индексные дескрипторы
Так как номер индексного дескриптора
уникален только в пределах одной файловой системы, то запись в
этой таблице идентифицируется как по номеру индексного дескриптора, так и по идентификатору файловой системы, который присваивается файловой системе при ее монтировании командой mount.
Взаимосвязь всех структур данных лучше всего показать на примере создания файла. Предположим, что программа должна создать файл с именем test в каталоге /home/user.
Последовательность шагов для выполнения этой задачи и действия операционной системы показаны на следующем рисунку.
Слайд 38Операции с файлами. Индексные дескрипторы
Для лучшего понимания, как выполняются
файловые операции в UNIX, проанализируем схему, показанную на рисунке (с
целью упрощения изложения предполагаем, что все операции выполняются без ошибок):
1. Выполняющийся процесс делает попытку создания файла test в каталоге /home/user при помощи системного вызова
fd = open("/home/user/test", O_RDWR | O_CREAT);
Слайд 39Операции с файлами. Индексные дескрипторы
Ядро UNIX создает новую запись
в таблице индексных дескрипторов и присваивает вновь созданному дескриптору уникальный
номер (в данном примере номер индексного дескриптора равен 279).
Эти действия выполняются на этапе (1).
2. Ядро добавляет номер дескриптора и имя файла в каталог /home/user (2).
Слайд 40Операции с файлами. Индексные дескрипторы
3. Ядро ищет в таблице
дескрипторов файлов процесса первую незадействованную позицию.
Если такая позиция есть,
то она будет использована для обращения к файлу (3).
Кроме этого, в системную таблицу файлов заносится ссылка на запись в таблице индексных дескрипторов, содержащая данные по открытому файлу.
Слайд 41Операции с файлами. Индексные дескрипторы
4. Ядро возвращает процессу номер
(индекс) позиции в таблице дескрипторов файлов процесса
в качестве дескриптора открытого файла (4).
Значение этого дескриптора присваивается переменной fd.
Слайд 42Операции с файлами. Индексные дескрипторы
Кроме этих действий, ядро выполняет
и целый ряд других операций. Так, значение счетчика ссылок в
индексном дескрипторе файла, точнее, в его копии, загруженной в память, увеличивается на 1.
Аналогично увеличивается и значение счетчика ссылок в системной таблице файлов.
Кроме этого, в таблицу файлов заносится информация о режиме, в котором открыт файл (в нашем случае файл открыт в режиме чтения/записи и если отсутствует на диске, то создается — это определяется вторым параметром функции open(), который равен O_RDWR | O_CREAT).
Слайд 43Операции с файлами. Индексные дескрипторы
Наконец, в записи таблицы
файлов формируется указатель текущей позиции в открытом файле.
Этот
указатель представляет собой смещение относительно начала файла позиции, начиная с которой будет происходить чтение/запись данных.
Рассмотренный нами процесс создания файла, естественно, является весьма упрощенным, тем не менее, он дает некоторое представление о принципах выполнения файловых операций в UNIX-системах.
Слайд 44Операции с файлами. Индексные дескрипторы
Каждый файл в
операционной системе UNIX характеризуется набором свойств
или, по-другому, атрибутов.
Мы уже сталкивались с ними при рассмотрении индексных дескрипторов, а сейчас проанализируем атрибуты файлов более подробно.
Набор общих атрибутов файлов представлен в следующей таблице.
Слайд 46Операции с файлами. Индексные дескрипторы
Перечисленные в таблице
атрибуты используются ядром для управления
файлами.
Например, при попытке доступа какого-либо пользователя к файлу ядро сравнивает его идентификаторы с идентификаторами uid и gid файла, чтобы установить какая категория разрешений должна быть задействована.
Все файлы имеют атрибуты, перечисленные в таблице, хотя, например, для файлов устройств атрибут "file size" не имеет смысла и всегда установлен в 0.
Слайд 47Операции с файлами. Индексные дескрипторы
Дополнительно для файлов устройств
устанавливаются такие атрибуты, как старший и
младший номера устройств.
Атрибуты назначаются файлу ядром в момент его создания и могут оставаться неизменными в течение всего времени его существования.
Некоторые атрибуты (тип файла, номер индексного дескриптора, идентификатор файловой системы, старший и младший номера устройств (для файлов устройств)) остаются неизменными, другие можно изменить либо программно, либо используя команды UNIX.
Слайд 48Операции с файлами. Индексные дескрипторы
Для получения информации
о файле программным способом можно
воспользоваться системным вызовом stat(). В листинге приведен исходный текст программы на С («си»), в которой на экран дисплея выводится размер файла, номер индексного дескриптора и значение счетчика жестких ссылок.
Листинг. Получение атрибутов файла при помощи функции stat() приведён на следующей странице.
Слайд 50Операции с файлами. Индексные дескрипторы
В качестве единственного параметра программа
принимает путевое имя файла (argv[1]).
После вызова функции stat() на
экран дисплея выводятся размер файла в байтах, номер индексного дескриптора и количество жестких ссылок на данный файл.
Все атрибуты файла помещаются в структуру statv, адрес которой является вторым параметром функции stat().
Слайд 51Операции с файлами. Индексные дескрипторы
Например, для файла test, находящегося
в каталоге приложения, были получены такие результаты (исходный текст скомпилирован
в исполняемый файл statdemo):
# ./stat_demo test
----------test attributes----------
Size: 30
i-number: 123880
Re f.count: 1
Слайд 52Операции с файлами. Индексные дескрипторы
Обратите внимание на количество жестких
ссылок — оно равно 1, т. е. для обычного файла
без жестких ссылок счетчик ссылок всегда равен 1.
Если задать для файла test жесткую ссылку, например:
# ln test test_hard_link
Слайд 53Операции с файлами. Индексные дескрипторы
то после запуска программы stat_demo
получим следующий результат:
# ./stat_demo test
----------test attributes----------
Size: 30
i-number:
123880
Re f.count: 2
Слайд 54Операции с файлами. Индексные дескрипторы
Как видно из результата, количество
жестких ссылок равно 2 — фактически мы имеем
два имени для одного файла: test и test_hard_link.
Если бы мы создали символическую ссылку, счетчик ссылок остался бы равным 1, поскольку символическая ссылка представляет собой объект файловой системы со своим индексным дескриптором.
Слайд 55Операции с файлами. Индексные дескрипторы
Содержимое каталога можно просмотреть программным
способом, используя две библиотечные функции С («Си») — opendir()
и readdir().
Кроме этого, следует объявить в программе структуру типа dirent (для систем, совместимых с System V) или direct (для систем, совместимых с FreeBSD).
В этом конкретном случае программа работает в операционной системе Solaris, поэтому используется dirent.
Слайд 56Операции с файлами. Индексные дескрипторы
Структура (dirent или direct)
содержит поля "Номер индексного дескриптора" и "Имя файла", которые
присутствуют в каждой записи каталога (смотри рисунок).
Слайд 57Операции с файлами. Индексные дескрипторы
Следующая программа (назовем
ее opendir_demo), исходный текст которой представлен в
следующем листинге, выводит на экран дисплея имена файлов текущего каталога и номера их индексных дескрипторов.
Листинг получения содержимого записей текущего каталога представлен на следующей странице.
Слайд 59Операции с файлами. Индексные дескрипторы
Здесь для работы с открытым
каталогом служит указатель DIR*, возвращаемый функцией opendir(), единственным параметром которой
является путь к текущему каталогу.
Далее в цикле while читается содержимое записей каталога, которое помещается в структуру dp функцией readdir().
Цикл заканчивается, когда readdir() возвращает нулевое значение.
Поле d_name структуры dp содержит имя файла, а поле d_ino — номер индексного дескриптора.
Слайд 60Операции с файлами. Индексные дескрипторы
Вот каким может
быть результат выполнения программы opendir_demo:
Слайд 61Список литературы:
Юрий Магда. UNIX для студентов, Санкт-Петербург «БХВ-Петербург», 2007.
Unix и
Linux: руководство системного администратора, 4-е издание, 2012, Э. Немет, Г.
Снайдер, Т. Хейн, Б. Уэйли
Организация UNIX систем и ОС Solaris 9, Торчинский Ф.И., Ильин Е.С., 2-е издание, исправленное, 2016.
Слайд 62Спасибо за внимание!
Преподаватель: Солодухин Андрей Геннадьевич
Электронная почта: asoloduhin@kait20.ru