46
Известно, что:
•интерпретатор shell, запуская процесс, передает ему три дескриптора файлов: 0 — для чтения; 1 и 2 — для записи;
•функция open(...), в случае нормального завершения, возвращает наименьший доступный номер дескриптора;
Впрактических задачах часто возникает необходимость дублирования дескрипторов или открытие дескриптора с заданным номером. Например, серверные программы, принимая внешние к ним запросы, могут сами не иметь алгоритмов обработки файлов, а обращаются к другим программам. В этом случае:
•программа обработки алгоритма пищется и отлаживается при условии, что
ввод осущесвляется из дескриптора с номером 0, а вывод осуществляется в файл с дескриптором 1;
•серверная программа, для обработки запроса, создает дочерний процесс, ко-
торый: открывает файл ввода информации, закрывает дескриптор 0 и дублирует дескриптор открытого файла, обеспечивая ввод данных по дескриптору 0; аналогичные действия проводятся с вескриптором файла вывода; после этого, посредством одного из системных вызовов exec(…), осуществляется запуск программы обработки алгоритма.
Стандарт POSIX предоставляет два системных вызова дублирования дескрипторов файлов:
#include <unistd.h>
int dup (int oldfd);
int dup2(int oldfd, int newfd);
где oldfd — старый дескриптор файла; newfd — новый дескриптор файла.
Обе функции, в случае нормального завершения вызова:
•возвращают новый дескриптор файла;
•функция dup2(…), если требуется, предварительно закрывает старый деск-
риптор oldfd.
Старый и новый дескрипторы можно использовать друг вместо друга. Они имеют общие блокировки, указатель позиции в файле и флаги; например, если позиция в файле была изменена с помощью lseek, на одном из дескрипторов, то эта позиция также меняется и на втором.
Если произошла ошибка, то возвращается значение -1 и переменная errno устанавливается должным образом:
EBADF — oldfd не является открытым файловым дескриптором, или же newfd находится вне допустимого диапазона файловых дескрипторов;
47
EMFILE — процесс уже открыл максимальное количество файлов и пытается открыть еще один;
EINTR — Вызов dup2(...) был прерван каким-либо сигналом;
EBUSY — может случиться только в Linux, при вызове dup2(...) во время состязательных вызовов open(...) и dup(...).
Все ОС имеют системные вызовы, предназначенные для подключения и отключения внешних файловых систем к корневой файловой системе.
Стандарт POSIX определяет эти функции как:
#include <sys/mount.h>
int mount(const char *source, const char *target, unsigned mountflags);
int umount(const char *special);
где source — раздел блочного устройства или файл, содержащий файловую систему, известную ядру ОС;
target — директория, присутствующая в файловой системе, к которой монтируется внешняя файловая система;
mountflags — опции, с которыми монтируется файловая система;
special — это source или target, которые специфицируют точку монтирования.
При удачном завершении вызова возращаемое значение равно нулю.
При ошибке возвращается -1, а переменной errno присваивается номер ошибки.
Замечание
В предыдущих темах были подробно изучены утилиты mount и umount, предназначенные для аналогичных целей.
Программисту, без большой необходимости, не следует не следует использовать эти системные вызовы, потому что:
•основные привилегии создания структуры ФС и защита такой структуры обеспе-
чивается администратором ОС, что исключает необходимость оперативного монтирования и демонтирования из программы;
•синтаксис и реализация этих системных вызовов всегда специфичен для конкретной ОС, можно убедится при изучении man 2 mount и man 2 umount;
•в крайнем случае, можно с помощью дочернего процесса вызвать «родные» утилиты ОС, что повысит переносимость разработанного ПО.
48
Данный подраздел является завершающим в теоретической части темы №7 «Подсистема управления вводом-выводом».
Здесь мы изучим материал касающийся вопросов формирования и использования имен файлов.
Программисту и обычному пользователю, любая отдельная файловая система ОС представляется в виде иерархии имен файлов, в которой особое место занимают
файлы типа директорий (каталогов):
•именно с корневого каталога начинается каждая файловая система, разме-
щенная на внешних носителях информации; например, в файловой системе EXT2FS, изученной в теме 4 [6, подраздел 1.5], за корневым каталогом закреплен индексный дескриптор (i-узел, i-node) с номером 2;
•именно к каталогам монтируются разные файловые системы, образуя еди-
ное дерево файловой системы ОС, вершина которой (корневой каталог VFS) формируется в ядре ОС;
•именно в записях каталогов имя файла (name) связано с i-узлом (inode).
Встандарте POSIX для создания и удаления каталогов определяет два системных вызова: mkdir(...) и rmdir(...).
Системный вызов mkdir(...) имеет синтаксис:
#include <sys/stat.h>
#include <sys/types.h>
int mkdir(const char *pathname, mode_t mode);
где pathname — имя создаваемой каталога;
mode — задает права доступа, которые получит зданный каталог; эти права стандартным образом модифицируются с помощью umask: права доступа оказываются равны mode & ~umask.
Созданный каталог принадлежит фактическому владельцу процесса и наследует его права. При успешном завершении возвращается ноль или -1, если произошла ошибка. В этом случае errno устанавливается следующим образом:
EPERM - файловая система, содержащая pathname, не поддерживает создание каталогов;
EEXIST — pathname уже существует: это не обязательно каталог, а например, символической ссылка;
•EFAULT — pathname указывает за пределы доступного адресного пространства;
•EACCES — родительский каталог не позволяет запись, или же один из каталогов, перечисленных в pathname, не позволяет поиск (выполнение);
•ENAMETOOLONG — pathname слишком длинно;
49
•ENOENT — компонент пути pathname не существует или является висячей символической ссылкой;
•ENOTDIR — компонент пути, использованный как каталог в pathname, в действительности таковым не является;
•ENOMEM — ядру не хватило памяти;
•EROFS — файл находится на файловой системе, смонтированной только для чтения;
•ELOOP — pathname является зацикленной символической ссылкой, то есть при подстановке возникает ссылка на неё саму;
•ENOSPC — на устройстве, содержащем pathname, нет места для создания
новой записи в каталоге; это может произойти также, если исчерпана квота дискового пространства пользователя.
Замечание
Создать файл каталог можно и спомощью системного вызова open(...) или, например, mknod(...), но такой каталог будет «неполноценным», поскольку в нем будут отсутствовать файлы «.» и «..».
Системный вызов rmdir(...) имеет синтаксис:
#include <unistd.h>
int rmdir(const char *pathname);
где pathname — имя удаляемого каталога, который должен быть пустым, кроме файлы «.» и «..».
В случае успеха возвращается ноль. При ошибке возвращается -1, а errno устанавливается следующим образом:
•EPERM — файловая система, содержащая pathname, не поддерживает удаление каталогов;
•EFAULT — pathname указывает за пределы доступного адресного пространства;
•EACCES — доступ на запись в каталоге, содержащем pathname, не разрешен
для текущего эффективного идентификатора пользователя, или же один из каталогов в pathname не разрешает поиск (выполнение);
•EPERM — в правах доступа к каталогу, содержащему pathname, включен бит "липкости" (S_ISVTX), а эффективный идентификатор пользователя не совпадает ни с владельцем удаляемого файла, ни с владельцем каталога, который его содержит, или же pathname является каталогом;
•ENAMETOOLONG — pathname слишком длинно;
•ENOENT — одна из частей пути pathname не существует или является висячей символической ссылкой;
•ENOTDIR — компонент пути, использованный как каталог в pathname, в действительности таковым не является;
•ENOTEMPTY — pathname содержит какие-либо еще, кроме записей . или .. ;
50
•EBUSY — pathname является текущим рабочим или корневым каталогом какого-либо процесса;
•ENOMEM — ядру не хватило памяти;
•EROFS — файл находится на файловой системе, смонтированной только для чтения;
•ELOOP — pathname является зацикленной символической ссылкой, то есть при подстановке возникает ссылка на неё саму.
Для удаления имен файлов, в стандарте POSIX, предусмотрен системный вызов unlink(...), имеющий следующий синтаксис:
#include <unistd.h>
int unlink(const char *pathname);
где pathname — имя удаляемого файла.
В случае успеха возвращается ноль. При ошибке возвращается -1, а errno устанавливается следующим образом:
•EACCES — доступ на запись в каталог, содержащий pathname не разрешён
для эффективного идентифкатора пользователя текущего процесса или права на один из каталогов в pathname не позволяют поиск (выполнение);
•EPERM или EACCES — каталог, содержащий pathname имеет установленный sticky-бит (S_ISVTX) и эффективный идентификатор пользователя текущего процесса не совпадает и с идентификатором пользователя удаляемого файла и с идентификатором пользователя каталога, в котором содержится этот файл;
•EPERM (только в Linux) — файловая система не разрешает удаление файлов;
•EPERM — система не разраешает удаление каталогов или удаление каталогов
требует превилегий, которыми не обладает текущий процесс: этот код ошибки появился в POSIX;
•EISDIR — pathname указывает на каталог: это не-POSIX значение, возвращаемое в Linux начиная с 2.1.132;
•EBUSY (нет в Linux) — файл pathname не может быть удалён, потому что он
используется системой или другим процессом и текущая реализация вызова считает, что это ошибка;
•EFAULT — pathname указывает за пределы доступного вам адресного пространства;
•ENAMETOOLONG — pathname имеет слишком большую длину;
•ENOENT — какой-либо компонент в pathname не существует или является битой ссылкой или в pathname пусто;
•ENOTDIR — какой-либо компонент, используемый как каталог в pathname, на самом деле не является каталогом;
•ENOMEM — не хватает памяти;
•EROFS — pathname указывает на файл в файловой системе доступной только для чтения;
•ELOOP — слишком много символьных ссылок было обнаружено при транс-