16
Приложения, соответствующие стандарту POSIX, могут быть одно- и много- процессными, с возможностью динамической адаптации конфигурации к свойствам целевой платформы. Далее перечислим основные важные элементы стандартизации без подробного объяснения.
Стандартизованы средства порождения и завершения процессов, смены их программ, опроса и/или изменения разнообразных характеристик. Процессы можно приостанавливать и активизировать в заданное время.
Необходимая степень детерминизма выполнения приложений достигается благодаря средствам поддержки реального времени, к которым относятся управление дисциплиной выделения процессоров, сигналы реального времени, удержание страниц в оперативной памяти, таймеры высокого разрешения и другие.
Функции для работы с файлами удовлетворяют потребности приложений в чтении и записи долговременных данных, защите таких данных от несанкционированного доступа:
•Механизм блокировки фрагментов файлов позволяет обеспечить атомарность транзакций.
•Асинхронный ввод/вывод дает возможность совмещать операции обмена, оптимизируя тем самым приложения.
•С помощью множества служебных программ можно относительно легко организовать сложную обработку данных.
•Тщательно проработаны вопросы доступа к внешним устройствам, которые подсоедины по последовательным линиям, особенно к терминалам.
Стандартизованный командный язык shell обеспечивает адекватное средство для написания небольших мобильных процедур и их быстрой интерактивной отладки.
Выделены механизмы конвейеров, позволяющие объединять команды в цепочки с фильтрацией промежуточных результатов.
Служебные программы образуют развитую среду выполнения для shell-процедур. Фоновый режим выполнения программ позволяет организовать одновременное выполнение нескольких программ и взаимодействие с ними.
Стандарт POSIX стандартизует интерфейс командной строки. Вероятно, в будущих версиях стандарта будет регламентирован графический интерфейс.
Для многопользовательских систем, POSIX регламентирует различные средства
непосредственного и почтового обмена информацией.
Стандарт POSIX - обязательный элемент современной дисциплины разработки прикладных систем.
17
Рассмотрев основные понятия и требования стандарта POSIX, можно перейти к непосредственному обсуждению системных вызовов ОС, по управлению файлами и файловыми системами.
Замечание
Изучение данного вопроса предполагает, что студент усвоил и повторил учебный материал темы 4 «Управление файловыми системами ОС» [4].
Теперь перейдем к изучению основных системных вызовов, разделенных на две группы, список которых приведен на рисунке 1.5:
•управление файлами;
•управление каталогами и файловыми системами.
Изучение назначения и возможностей этих функций является предметом изучения данной темы.
Рисунок 1.5 — Системные функции подсистемы ввода-вывода
Замечание
Отметим, что наряду с низкоуровневыми системными вызовами, приведенными на рисунке 1.5, для работы с файлами и файловыми системами используются более высокоуровневые функции, которые обеспечивают буферизованный ввод-вывод и подробно изложены в любом учебнике по языку С. Считается, что студент студент уже имеет опыт работы с ними и, при необходимости, использует учебник [5] как справочное пособие.
18
В данном подразделе будут изучены только первые шесть функций, которые касаются непосредственно управления файлами.
Замечание
При изучении системных функций следует помнить, что все приводимые примеры и ограничения привязаны к архитектуре ОС УПК АСУ.
Все системные функции ввода-вывода используют целочисленное значение дескриптора файла. Это значение формируется в ядре ОС и индивидуально привязывается к конкретному процессу.
Нумерация значений дескрипторов для каждого процесса начинается с нуля и определяется в момент открытия файлов, из условия минимального значения доступного (незанятого) дескриптора.
Когда файл закрывается, то соответсвующий ему номер дескриптора освобождается и используется для открытия других файлов.
Все остальные системные функции, кроме функции stat(...), используют уже открытый дескриптор файла.
Замечание
Указанные правила работы с дескриторами файлов одинаковы для всех ОС и широко используются в различных алгоритмах, в частности, при дублировании дескрипторов для различных целей. Поэтому студент постоянно должен помнить об этих правилах.
Системный вызов open(...) открывает некоторый файл и, в случае успешного завершения, возвращает целочисленный номер дескриптора.
Общий формат вызова имеет вид:
#include <sys/types.h>
#include <sys/stat.h> #include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
Допускается использование следующих аргументов:
pathname — строковый указатель на имя файла, заданного в соответсвии с требованиями конкретной ОС;
flags — целочисленное значение, определяющее режимы открытия файла, которые обычно задаются конкатенацией битовых масок:
•O_APPEND — добавление информации в конец файла;
•O_CREAT — создание файла, если он отсутсвует;
•O_RDONLY — только для чтения из файла;
•O_WRONLY — только для записи в файл;
19
•O_RDWR — для чтения и записи в файл;
•O_TMPFILE — после закрытия файл будет удален, а также многие другие.
mode — целочисленное значение, определяющее режимы доступа к файлу, как это было описано в теме 4, например, восьмеричное значение 0666 разрешает доступ на чтение и запись всем пользователям.
Вслучае ошибочного завершения, функция open(...) возвращает отрицательное значение -1, а переменная errno содержит код ошибки, котрый следует анализировать отдельно.
Замечание
Функция open(...) содержит множество режимов открытия файлов, которые могут быть несовместимы друг с другом, а также множество вариантов сообщений об ошибках, поэтому для полного ее изучения следует пользоваться действующим руководством ОС, например, используя команду: man 2 open
Системный вызов close(...) закрывает открытый файл и освобождает номер дескриптора для последующего использования. Формат этого системного вызова имеет вид:
#include <unistd.h> int close(int fd);
Вслучае успешного завершения вызова, возвращается целочисленное значение 0.
Вслучае ошибки — целочисленное значение -1, а переменная errno содержит код ошибки:
•EBADF — аргумент функции не является правильным дескриптором файла;
•EINTR — закрытие файла было прервано сигналом;
•EIO — общая ошибка ввода-вывода.
Замечание
Несмотря на кажущуюся простоту, системный вызов close(...) выполняет очень важ-ные функции:
•он универсален, поскольку применим ко всем видам файлов;
•корректно завершает работу с файлом, записывая при необходимости все данные на устройство ввода-вывода;
•неявно вызывается ядром ОС, когда процесс завершает свою работу.
20
Системный вызов read(...) читает заданное число байт из файла, определенного его дескриптором.
В случае успешного завершения операции чтения, данные помещаются во внешний буфер и возвращается реальное число прочитанных байт.
Общий формат вызова имеет вид:
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
Для вызова этой функции используется три аргумента:
•fd — целочисленный номер дескриптора открытого файла;
•buf — указатель на внешний буфер, в который записываюся читаемые байты;
•count — количество байт запрашиваемых для чтения из файла.
Функция read(...) читает байты с текущей позиции файла:
•если смещение текущей позиции равно или больше размера файла, то возвращается значение 0;
•если данные недоступны, но конец файла не достигнут, то ожидается поступление данных, что в общем случае определяется режимом открытия файла;
•в случае ошибки чтения, возвращается значение -1 и необходимо анализировать переменную errno;
•если размер параметра count превышает значение константы SSIZE_MAX, то результат выполнения операции не специфицирован.
Замечание
При использовании функции read(...) необходимо анализировать системные ограничения конкретной ОС: максимальный размер выделяемого буфера для записи читаемых данных и максимальное число читаемых данных за одну операци. Например, для ОС УПК АСУ:
•тип ssize_t определяет целое число размерности 8 байт;
•BUFSIZ — размер выделяемого по умолчанию буфера равен 4096 байт;
•SSIZE_MAX — соответствует максимальному значению 64-битного целого.
Системный вызов write(...) записывает заданное число байт из буфера в файл, определенный его дескриптором. В случае успешного завершения операции, данные помещаются в файл, начиная с текущей позиции, и возвращается реальное число записанных байт. Общий формат вызова имеет вид:
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
В этом системном вызове используются теже аргументы, что и в рассмотренной ранее функции read(...). В случае ошибки записи, возвращается значение -1 и