Вперед Назад Содержание

8. Ввод-Вывод низкого уровня

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

8.1 Открытие и Закрытие Файлов

Этот раздел описывает примитивы для открытия и закрытия файлов, используя дескрипторы файла. Функции creat и open объявлены в файле 'fcntl.h', в то время как close объявлена в 'unistd.h'.

-Функция: int open (const char *filename, int flags[, mode_t mode])

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

Аргумент flags управляет тем, как файл будет открыт. Это ­ битовая маска; Вы создаете значение поразрядным ИЛИ соответствующих параметров (используя оператор `|' в C).

Аргумент flags должен включять точно одно из этих значений, для задания режима доступа к файлу:

       O_RDONLY  Открывает  файл  для  чтения.
       O_WRONLY  Открывает  файл  для  записи.
       O_RDWR    Открывает файл,  и для чтения и для записи.
Аргумент flags может также включать любую комбинацию этих флагов:
       O_APPEND Если установлен,  то все операции записи запишут данные в
                 конец файла, расширяя его, независимо от текущей файловой
                 позиции.
       O_CREAT  Если установлен,  будет создан файл,  если он еще  не
                существует.
       O_EXCL   Если  и  O_CREAT  и  O_EXCL - установлены,  то open выдает
                ошибку, если заданный файл уже существует.
       O_NOCTTY Если filename имя терминала,  не делайте его терминалом
                управления для  процесса.  См.  Главу  24  [Управление
                заданиями],  для уточнения информации относительно того,
                что означает терминал управления.
       O_NONBLOCK Устанавливает  режим  неблокирования.  Эта опция обычно
                полезна для специальных файлов  типа  FIFO  (см.  Главу  10
                [Каналы и FIFO]) и устройств типа терминалов.
                Обычно,  для этих файлов  open блокируется, пока файл не 'готов'.
                Если O_NONBLOCK установлен,  open возвращается немедленно.
                O_NONBLOCK бит  также  воздействует  на  чтение  и  на запись:  он
                разрешает им  возвращаться  немедленно  с  состоянием  ошибки,
                если  не имеется никакого доступного ввода, или если вывод не
                может быть записан.
       O_TRUNC Если файл существует и открыт для записи, усекает это, до
               нулевой длины.  Эта  опция  полезна  только для регулярных
               файлов,  а не специальных файлов типа каталогов или FIFO. Для
               получения более подробной информации об этих символических
               константах см. Раздел 8.10 [Флаги Состояния  Файла].
Нормальное возвращаемое значение open - неотрицательный описатель файла типа integer. В случае ошибки возвращается значение -1. В дополнение к обычным синтаксическим ошибкам имени файла (см. Раздел 6.2.3 [Ошибки Имени файла]), следующие errno условия ошибки определены для этой функции:
EACCES

Файл существует, но не читаем/перезаписываем как запрошено аргументом flags.

EEXIST

И O_CREAT и O_EXCL - установлены, а именованный файл уже существует.

EINTR

Операция open была прервана сигналом. См. Раздел 21.5 [Прерванные Примитивы] 3.

EISDIR

Аргумент flags задает доступ для записи, а файл - каталог.

EMFILE

Процесс имеет слишком много открытых файлов.

ENFILE

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

ENOENT

Именованный файл не существует, но O_CREAT не определен.

ENOSPC

Каталог или файловая система, которая содержала бы новый файл,не может быть расширена, т. к. там не осталось дискового пространства.

ENXIO

O_NONBLOCK и O_WRONLY установлены в аргументе flags, файл, именованный filename - FIFO (см. Главу 10 [Каналы и FIFO]), и никакой процесс имеет файл открытым для чтения.

EROFS

Файл постоянно находится в файловой системе только для чтения и любой из O_WRONLY, O_RDWR, O_CREAT, и O_TRUNC ­ установлены в аргументе flags.

Функция open - основной примитив для fopen и freopen функций, которые создают потоки.

-Функция: int creat (const char *filename, mode_t mode)

Эта функция устаревает. Обращение: creat (filename, mode) эквивалентно: open (filename, O_WRONLY | O_CREAT | O_TRUNC, mode)

-Функция: int close (int filedes)

Функция закрывает дескриптор файла filedes. Закрытие файла имеет следующие последствия:

  • Описатель файла освобожден.
  • Любые блокировки записи, принадлежащие процессу на файле разблокирутся.
  • Когда все описатели файла, связанные с каналом или в порядке поступления были закрыты, любые читаемые обратно данные отброшены.
Нормальное возвращаемое значение - 0; значение -1 возвращается в случае ошибки.

Следующие errno условия ошибки определены для этой функции:

  • EBADF Filedes аргумент - не допустимый дескриптор файла.
  • EINTR Обращение было прервано сигналом. См. Раздел 21.5 [Прерванные Примитивы]. Вот пример правильной обработки EINTR:
           TEMP_FAILURE_RETRY (close (desc));
Для закрытия потока, вызовите fclose (см. Раздел 7.4 [Закрытие Потоков]) вместо того, чтобы пробовать закрыть основной описатель файла. Она дописывает любой буферизированный вывод и модифицирует объект потока указывая, что он закрыт.

8.2 Примитивы Ввода и Вывода

Этот раздел описывает функции для выполнения ввода и вывода с помощьюю дескрипторов файла: read, write, и lseek. Эти функции объявлены в файле ' unistd.h '.

-Тип данных: ssize_t

Этот тип данных используется для представления размеров блоков, которые могут быть прочитаны или записаны одиночной операцией. Он подобен size _t, но должен быть знаковым типом.

-Функция: ssize_t read (int filedes, void *buffer, size_t size)

Функция read читает до size байтов из файла с описателем filedes, сохраняя результаты в буфере. (Это - не обязательно символьная строка и не имеется никакого добавленного пустого символа завершения.)

Возвращаемое значение - число байтов фактически прочитанных. Оно может быть меньше чем size.

Нулевое значение указывает конец файла (за исключением того, если значение аргумента size является также нулем). Это не является ошибкой.

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

В случае ошибки read возвращает -1. Следующие errno условия ошибки определены для этой функции:

EAGAIN

Обычно, когда никакой ввод недоступен, read ждет какого-нибудь ввода. Но если установлен флаг O_NONBLOCK (см. Раздел 8.10 [Флаги Состояния Файла]), read возвращается немедленно не читая никаких данных, и сообщает эту ошибку.

Примечание о Совместимости: Большинство версий BSD UNIX использует различный код ошибки для EWOULDBLOCK. В библиотеке GNU, EWOULDBLOCK - синоним EAGAIN, так что не имеет значения, акое название Вы используете.

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

EBADF

Filedes аргумент - не допустимый описатель файла.

EINTR

Чтение было прервано сигналом, в то время как он ждал ввода.

См. Раздел 21.5 [Прерванные Примитивы]. EIO Для многих устройств, и для файлов на диске, этот код ошибки указывает аппаратную ошибку. EIO также происходит, когда фоновый процесс пробует читать с терминала управления, и нормальное действие остановки процесса, т. е. Посылка сигнала SIGTTIN не работает. Это может случиться, если сигнал блокируется или игнорируется, или т. к. группа процесса - orphaned. См. Главу 24 [Управление заданиями].

Функция read - основной примитив для всех функций, которые читают из потоков, типа fgetc.

-Функция: ssize_t write (int filedes, const void *buffer, size_t size)

Функция write пишет до size байтов из буфера в файл с описателем filedes. Данные в буфере - не обязательно символьная строка и вывод пустого символа подобен любому другому.

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

EAGAIN

Обычно, write блокируется до завершения операции записи. Но если для файла устанавлен O_NONBLOCK флаг (см. Раздел 8.7 [Операции Управления]), она возвращается немедленно не позводя записи любых данных, и сообщает эту ошибку. Пример ситуации, котаря могла бы вызывать блокировани процесса на выводе, запись в терминальное устройство, которое поддерживает управление потоком данных, где вывод был приостановлен получением символа STOP.

Примечание Совместимости: Большинство версий BSD UNIX использует различный код ошибки для EWOULDBLOCK. В библиотеке GNU, EWOULDBLOCK ­ синоним для EAGAIN, так что не имеет значения, какое название Вы используете.

EBADF

Аргумент Filedes - не допустимый дескриптор файла.

EFBIG

Размер файла больший чем реализация, может поддерживать.

EINTR

Операция write была прервана сигналом, в то время как она была блокирована, и ждала завершения. См. Раздел 21.5 [Прерванные Примитивы].

EIO

Для многих устройств, и для файлов на диске, этот код ошибки указывает аппаратную ошибку. EIO также происходит, когда фоновый процесс пробует читать с терминала управления, и нормальное действие остановки процесса, посылая сигнал SIGTTIN не работает. Это может случиться, если сигнал блокируется или игнорируется, или т. к. группа процесса - orphaned. См. Главу 24 [Управление заданиями].

ENOSPC

переполнение устройства.

EPIPE

Эта ошибка возвращается, когда Вы пробуете писать в канал или в FIFO, который не открыт для чтения процессом. Когда это случается, сигнал SIGPIPE также посылается в процесс; см. Главу 21 [Обработка Сигнала].

Если Вы не предотвращаете EINTR ошибки, Вам следует проверять errno после каждого выдающего ошибку обращения к write, и если ошибка была EINTR, Вы должны просто повторить обращение. См. Раздел 21.5 [Прерванные Примитивы]. Простой способ реализовать это - макрокомандой TEMP_FAILURE_RETRY, следующим образом:
       nbytes = TEMP_FAILURE_RETRY (write (desc, buffer, count));
Функция write - основной примитив для всех функций записи в поток, типа fputc.

8.3 Установка Файловой позиции Дескриптора

Точно как Вы можете устанавливать файловую позицию потока функцией fseek, Вы можете устанавливать файловую позицию дескриптора функцией lseek. Она определяет позицию в файле для следующей операции read или write. См. Раздел 7.15 [Позиционирование Файла], для подробной информации относительно файловой позиции и что это означает.

Для получения текущего значения файловой позиции из описателя, используйте lseek (desc, 0, SEEK_CUR).

-Функция: off_t lseek (int filedes, off_t offset, int whence)

Функция lseek используется, чтобы изменить файловую позицию файла с описателем filedes. Аргумент whence определяет, как смещение должно интерпретироваться, таким же образом как в функции fseek, и может быть одной из символических констант SEEK_SET, SEEK_CUR, или SEEK_END.

SEEK_SET

Определяет, что whence - яисло символов от начала файла.

SEEK_CUR

Определяет, что whence - число символов от текущей файловой позиции. Этот число может быть положительно или отрицательно.

SEEK_END

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

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

Вы можете устанавливать файловую позицию после текущего конца файла. Это делает файл больше; lseek никогда не изменяет файл. Но последующий вывод в ту позицию расширит файла.

Если файловая позиция не может быть изменена, или операция выполняется некоторым недопустимым способом, lseek возвращает значение -1. Следующие errno условия ошибки определены для этой функции:

EBADF

Filedes - не допустимый описатель файла.

EINVAL

Значение аргумента whence не допустимо, или возникающее в результате смещение файла не допустимо.

ESPIPE

filedes соответствует каналу или FIFO, который не может быть позиционирован. ( Могут иметься другие виды файлов, которые также не могут быть позиционированы, но в этих случаях поведение не определено.)

Функция lseek - основной примитив для fseek, ftell и rewind функций, которые функционируют на потоках вместо описателей файла.

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

   {
        int d1, d2;
        char buf[4];
        d1 = open ('foo', O_RDONLY);
        d2 = open ('foo', O_RDONLY);
        lseek (d1, 1024, SEEK_SET);
        read (d2, buf, 4);
      }
будет читать первые четыре символа файла 'foo'. (Код с обнаружением ошибок, необходимый для реальной программы был опущен здесь для краткости.)

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

 {
        int d1, d2, d3;
        char buf1[4], buf2[4];
        d1 = open ('foo', O_RDONLY);
        d2 = dup (d1);
        d3 = dup (d2);
        lseek (d3, 1024, SEEK_SET);
        read (d1, buf1, 4);
        read (d2, buf2, 4);
      }

Будет читать четыре символа, начиная с 1024-го символа 'foo', и еще четыре символа, начиная с 1028-го символа.

-Тип данных: оff_t

Это - арифметический тип данных, используемый, чтобы представить размеры файла. В системе GNU, он эквивалентен fpos_t или long int. Эти три синонима константы 'SEEK_...' существуют ради совместимости с более старыми BSD системами. Они определены в двух различных файлах: 'fcntl.h' и 'sys/file.h'.

L_SET синоним для SEEK_SET.

L_INCR синоним SEEK_CUR.

L_XTND синоним SEEK_END.

8.4 Дескрипторы и Потоки

Определяя дескриптор файла с помощью open, Вы можете создавать поток для него функцией fdopen. Вы можете получить основной описатель файла для существующего потока функцией fileno. Эти функции объявлены в заглавном файле 'stdio.h'.

-Функция: FILE * fdopen (int filedes, const char *opentype)

Функция fdopen возвращает новый поток для описателя файла filedes. Opentype аргумент интерпретируется таким же образом как в функции fopen (см. Раздел 7.3 [Открытие Потоков]), за исключением того, что опция 'b' не разрешается; это оттого, что GNU не делает никакого различия между текстом и двоичными файлами. Также, 'w' и 'w+' не вызывают усечение файла; они воздействуют только при открытии файла, а в этом случае, файл уже открыт. Вы должны удостовериться, что opentype аргумент соответствует фактическому режиму дескриптора файла.

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

Для примера, показывающего использование функции fdopen, см. Раздел 10.1 [Создание Канала].

-Функция: int fileno (FILE *stream)

Эта функция возвращает описатель файла, связанный с указанным потоком. Если обнаружена ошибка (например, если поток не допустим) или если поток, не делает ввод - вывод в файл, fileno возвращает - 1.

Имеются также символические константы, определенные в 'unistd.h' для описателей файла, принадлежащих к стандартным потокам stdin, stdout, и stderr; см. Раздел 7.2 [Стандартные Потоки].

STDIN_FILENO

Эта макрокоманда имеет значение 0, которое является дескриптором файла для стандартного ввода.

STDOUT_FILENO

Эта макрокоманда имеет значение 1, которое является дескриптором файла для стандартного вывода.

STDERR_FILENO

Эта макрокоманда имеет значение 2, которое является дескриптором файла для стандартного вывода ошибки.

8.5 Опасности Смешивания Потоков и Дескрипторов

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

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

Связанные Каналы

Каналы, которые исходят из одного открытия, совместно используют ту же самую файловую позицию; мы называем их связанными каналами. Связанные каналы кончаются, когда Вы делаете поток из дескриптора, используя fdopen, и когда Вы получаете описатель из потока с fileno, и когда Вы копируете описатель с dup или dup2. Для файлов, которые не поддерживают, произвольный доступ, типа терминалов и канадов, все каналы действительно связаны. На файлах прямого доступа, все выходные потоки конкатенирующего типа действительно связаны друг с другом.

Если Вы использовали поток для ввода - вывода, и Вы хотите делать ввод - вывод, используя другой канал (или поток или дескриптор) который связан с этим, Вы должны сначала очистить поток, который Вы использовали. См. Раздел 8.5.3 [Очистка Потоков].

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

Независимые Каналы

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

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

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

Двум каналам невозможно иметь отдельные указатели файла для файла, который не поддерживает произвольный доступ. Таким образом, каналы для чтения или записи в такие файлы всегда связаны. Каналы конкатенирующего типа также всегда связаны. Для этих каналов, соблюдайте правила связанных каналов; см. Раздел 8.5.1 [Связанные Каналы].

Очистка Потоков

В системе GNU, Вы можете очистить любой поток функцией fclean:

-Функция: int fclean (FILE *stream)

Очищает указанный поток так, чтобы буфер был пуст. На других системах, Вы можете использовать fflush, чтобы oчистить поток в большинстве случаев. Вы можете пропускать fclean или fflush, если Вы знаете, что поток уже пуст. Поток пуст всякий раз, когда буфер пуст. Например, небуферизованный поток всегда пуст. Входной поток, который находится в конце файла, пуст. Буферизированный строчно поток пуст, когда последний выходной символ был символ перевода строки.

Имеется один случай, в котором сброс потока является невозможным на большинстве систем. Тогда, когда поток делает ввод из файла, не являющегося файлом прямого доступа. Такие потоки обычно читают вперед, и когда файл ­ не произвольного доступа, то нет никакого способа сбросить обратно данные излишка уже прочтитанные. Когда входной поток читается из файла прямого доступа, fflush oчищает поток, но оставляет указатель файла в непредсказуемом месте; Вы должны установить указатель файла перед выполнением любого дальнейшего ввода - вывода. В системе GNU, используя fclean Вы избежите обе эти проблемы.

fflush также делает закрытие выходного потока, так что это ­ допустимый способ очистки выходного потока. В системе GNU, закрытие входного потока вызывает fclean.

Вы не нуждаетесь в чистке потока перед использованием дескриптора для операций управления, типа установки режимов терминала; эти операции не воздействуют на файловую позицию и не зависят от нее. Вы можете использовать любой дескриптор для этих операций, и на все каналы воздействовать одновременно. Однако, текст уже 'выведенный' в поток но все еще буферизируемый потоком будет подчинен новым режимам терминала. Чтобы удостовериться что 'прошлый' вывод подчинен установкам терминала, которые были в действительности во время, сбрасывайте выходные потоки для того терминала перед установкой режимов. См. Раздел 12.4 [Режимы Терминала].

8.6 Ожидание Ввода или Вывода

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

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

Лучшее решение состоит в том, чтобы использовать функцию выбора. Она блокирует программу до ввода или вывода по заданному набору описателей файла, или по таймеру. Это средство объявлено в заглавном файле 'sys/types.h'.

Наборы описателей файла для функции выбора определены как объекты fd_set. Вот описание типа данных и некоторых макрокоманд для управления этих объектов.

fd_set (тип данных)

Этот тип данных представляет наборы описателей файла для функции выбора. Это - фактически битовый массив.

int FD_SETSIZE (макрос)

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

void FD_ZERO (fd_set *set) (макрос)

Эта макрокоманда инициализирует набор наборов описателей файла, как пустое множество.

void FD_SET (int filedes, fd_set *set) (макрос)

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

void FD_CLR (int filedes, fd_set *set) (макрос)

Эта макрокоманда удаляет filedes из набора дескрипторов файлов.

int FD_ISSET (int filedes, fd_set *set) (макрос)

Эта макрокоманда возвращает значение отличное от нуля (истина), если filedes - элемент набора описателей файлов, и нуль (ложь) иначе.

Вот описание функции выбора непосредственно.

        int select (int nfds, fd_set *read_fds, fd_set *write_fds, fd_set *except_fds,  struct  timeval  *timeout)
Функция выбора блокирует процесс вызова до наличия действий на любом из заданных наборов описателей файла, или пока период блокировки по времени не истечет.

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

'Исключительные условия' не означают, что ошибки сообщаются немедленно, когда выполнен ошибочный системный вызов. Они включают условия, типа присутствия срочного сообщения на гнезде. (См. Главу 11 [Гнезда], для уточнения информации о срочных сообщениях.)

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

Блокировка по времени определяет максимальное время ожидания. Если Вы передаете пустой указатель в качестве этого аргумента, это означает, что блокировать неопределенно, пока один из описателей файла не готов. Иначе, Вы должны обеспечить время в формате struct timeval; см. Раздел 17.2.2 [Календарь с высоким разрешением]. Определите нуль как время (struct timeval содержащий все нули) если Вы хотите выяснять, какие описатели готовы в данный момент, без ожидания.

Нормальное возвращаемое значение - общее число готовых описателей файла во всех наборах. Каждый из наборов аргументов записан поверх информацией относительно описателей, которые являются готовыми для соответствующей операции. Таким образом, чтобы видеть, готов ли данный описатель desc для ввода, используйте FD_ISSET (desc, read_fds) после возврата select.

Если select возвращается, потому что период блокировки по времени истекает, то возвращаемое значение - нуль.

Любой сигнал заставит select возвращаться немедленно. Так, если ваша программа использует сигналы, Вы не можете полагаться на select, чтобы ждать полное заданное время. Если Вы хотите убедиться в правильности ожидания, Вы должны проверить EINTR и повторять select с расчетной блокировкой по времени, основанной на текущем времени. См. пример ниже. См. также Раздел 21.5 [Прерванные Примитивы].

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

EBADF

Один из набора описателей файла определил недопустимый дескриптор файла.

EINTR

Операция была прервана сигналом. См. Раздел 21.5 [Прерванные Примитивы].

EINVAL

Аргумент блокировки по времени недопустим; один из компонентов отрицателен или слишком большой.

Примечание о Переносимости: функция select - BSD возможность UNIX. Вот пример, показывающий, как Вы можете использовать select, чтобы установить период блокировки по времени для чтения из дескриптора файла. Input_timeout функция блокирует процесс вызова, пока ввод не доступен (на описателе файла), или пока период блокировки по времени не истек.

      #include <stdio.h>
      #include <unistd.h>
      #include <sys/types.h>
      #include <sys/time.h>

      int
      input_timeout (int filedes, unsigned int seconds)
      {
        fd_set set;
        struct timeval timeout;

        /* Инициализируем набор дескрипторов файлов. */
        FD_ZERO (&set);
        FD_SET (filedes, &set);

        /* Инициализируем структуру timeout. */
        timeout.tv_sec = seconds;
        timeout.tv_usec = 0;

        /* `select' returns 0 if timeout, 1 if input available, -1 if error.
 */
        return TEMP_FAILURE_RETRY (select (FD_SETSIZE,
                                           &set, NULL, NULL,
                                           &timeout));
      }

      int
      main (void)
      {
        fprintf (stderr, 'select returned %d.\n',
                 input_timeout (STDIN_FILENO, 5));
        return 0;
      }

8.7 Операции Управления Файлами

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

Второй аргумент fcntl функции - команда, которая определяет которую операцию выполнить. Функция и макрокоманды, которые обозначают различные флаги, объявлены в заглавном файле 'fcntl.h'. (Многие из этих флагов также используются функцией open; см. Раздел 8.1 [Открытие и Закрытие Файлов].)

-Функция: int fcntl (int filedes, int command, ...)

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

Вот краткий список различных команд.

F_DUPFD

Дублирует дескриптор файла (возвращает другой дескриптор файла, указывающий на тот же самый открытый файл). См. Раздел 8.8 [Дублирование Дескрипторов].

F_GETFD

Получает флаги, связанные с описателем файла. См. Раздел 8.9 [Дескрипторные Флаги]. F_SETFD Устанавливает флаги, связанные с дескриптором файла. См. Раздел 8.9 [Дескрипторные Флаги].

F_GETFL

Получает флаги, связанные с открытым файлом. См. Раздел 8.10 [Флаги Состояния Файла].

F_SETFL

Устанавливает флаги, связанные с открытым файлом. См. Раздел 8.10 [Флаги Состояния Файла].

F_GETLK

Получает блокировку файла. См. Раздел 8.11 [Блокировки Файла].

F_SETLK

Устанавливает или снимает блокировку файла. См. Раздел 8.11 [Блокировки Файла].

F_SETLKW

Подобен F_SETLK, но ждет завершения. См. Раздел 8.11 [Блокировки Файла].

F_GETOWN

Получает процесс или ID группы процессов, чтобы получить сигналы SIGIO. См. Раздел 8.12 [Ввод Прерывания].

F_SETOWN

Устанавливает процесс или ID группы процессов, чтобы получить сигналы SIGIO. См. Раздел 8.12 [Ввод Прерывания].

8.8 Дублирование Дескрипторов

Вы можете дублировать дескриптор файла, или зарезервировать другой дескриптор файла, который относится к тому же самому открытому файлу, что и первый. Двойные дескрипторы совместно используют одну и ту же файловую позицию и один набор флагов состояния файла (см. Раздел 8.10 [Флаги Состояния Файла]), но каждый имеет собственный набор флагов дескриптора файла (см. Раздел 8.9 [Дескрипторные Флаги]).

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

Вы можете выполнять эту операцию, используя fcntl функцию с командой F_DUPFD, но имеются также удобные функции dup и dup2 для дублирования дескрипторов.

Fcntl функция и флаги объявлена в 'fcntl.h', в то время как прототипы для dup и dup2 находятся в файле 'unistd.h'.

-Функция: int dup (int old)

Эта функция копирует дескриптор old в первый доступный дескрипторный номер (первый номер не открытый сейчас). Это эквивалентно fcntl (old, F_DUPFD, 0).

-Функция: int dup2 (int old, int new)

Эта функция копирует дескриптор old в дескриптор new.

Если оld - недопустимый дескриптор, то dup2 не делает ничего. Иначе, новый дубликат old заменяет любое предыдущее значение дескриптора new, как будто он был закрыт сначала.

Если оld и new - различные числа, и old - допустимый дескрипторный номер, то dup2 эквивалентен:

                        close (new);
                        fcntl (old, F_DUPFD, new)
Однако, dup2 делает это автоматически; нет никакого момента в середине вызова dup2, когда new закрыт, а дубликата old еще нет.
-Макрос int F_DUPFD

Эта макрокоманда используется как аргумент команды fcntl, для копирования дескриптора файла, данного как первый аргумент.

Форма обращения в этом случае:

        fcntl (old, F_DUPFD, next_filedes)
Next_filedes аргумент имеет тип int и определяет, что возвращенный дескриптор файла должен быть следующим доступным больше или равным этому значению.

Возвращаемое значение из fcntl с этой командой - обычно значение нового дескриптора файла. Возвращаемое значение -1 указывает ошибку. Следующие errno условия ошибки определены для этой команды:

EBADF

Аргумент old недопустим.

EINVAL

Next_filedes аргумент недопустим.

EMFILE

Дескрипторов файла, доступных вашей программе, больще нет.

ENFILE

- не возможный код ошибки для dup2, потому что dup2 не создает новое открытие файла; двойные дескрипторные подпадают под ограничение, которое указывает ENFILE.

EMFILE

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

Вот пример, показывающий, как использовать dup2, чтобы делать переадресацию. Обычно, переадресация стандартных потоков (подобно stdin) выполняется командным интерпретатором или подобной программой перед вызовом одной из запускаемых функций (см. Раздел 23.5 [Выполнение Файла]) чтобы выполнить новую программу в дочернем процессе. Когда новая программа выполнена, она создает и инициализирует стандартные потоки, чтобы указать на соответствующие дескрипторы файла, прежде, чем функция main вызывается.

Так, чтобы переназначить стандартный ввод в файл, оболочка могла бы делать что - нибудь вроде:

                pid = fork ();
                if (pid == 0)
                        {
                                char *filename;
                                char *program;
                                int file;
                                .       . .
                                file = TEMP_FAILURE_RETRY (open
                                (filename, O_RDONLY));
                                dup2 (file, STDIN_FILENO);
                                TEMP_FAILURE_RETRY (close (file));
                                execv (program, NULL);
                        }

8.9 Флаги Дескриптора Файла

Флаги дескриптора Файла - разнообразные атрибуты дескриптора файла. Эти флаги связаны со специфическими дескрипторами файла, так, что если Вы создаете двойные дескрипторы файла из одиночного открытия файла, то каждый дескриптор имеет собственный набор флагов.

В настоящее время имеется только один флаг дескриптора файла: FD_CLOEXEC, который заставляет дескриптор быть закрытым если Вы используете любую из запускаемых функций (см. Раздел 23.5 [Выполнение Файла]).

Символы в этом разделе определены в заглавном файле 'fcntl.h'.

int F_GETFD (макрос)

Эта макрокоманда используется как аргумент команды fcntl, чтобы определять, что она должна возвратить флаги дескриптора файла, связанные с filedes аргументом.

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

В случае ошибки, fcntl возвращает -1. Следующие errno условия ошибки определены для этой команды:

EBADF filedes аргумент недопустим.

int F_SETFD (макрос)

Эта макрокоманда используется как аргумент команды fcntl, для определения, что она должна установить флаги дескриптора файла, связанные с filedes аргументом. Она требует третий int аргумент, чтобы определить новые флаги, так что форма обращения:

        fcntl (filedes, F_SETFD, new_flags)
Нормальное возвращаемое значение из fcntl с этой командой ­ неопределенное значение отличное от -1, которое указывает ошибку. Флаги и условия ошибки - также как для команды F_GETFD.

Следующая макрокоманда определена для использования как флаг дескриптора файла с fcntl функцией. Значение - константа integer пригодное для использования как значение битовой маски.

int FD_CLOEXEC (макрос)

Этот флаг определяет, что дескриптор файла должен быть закрыт, когда вызывается запускаемая функция; см. Раздел 23.5 [Выполнение Файл]. Когда дескриптор файла размещен (как с open или dup), этот бит первоначально очищен на новом описателе файла, означая, что дескриптор останется в живых в новой программе после запуска.

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

                int
                set_cloexec_flag (int desc, int value)
                {
                        int oldflags = fcntl (desc, F_GETFD, 0);

                        if (oldflags < 0)
                                        return oldflags;
                        /* Установим только флаг который мы хотим
                        установить. */
                        if (value != 0)
                                        oldflags |= FD_CLOEXEC;
                        else
                                        oldflags &= ~FD_CLOEXEC;

                        return fcntl (desc, F_SETFD, oldflags);
                }

8.10 Флаги Состояния Файла

Флаги состояния Файла используются, чтобы определить атрибуты открытия файла. В отличие от флагов дескриптора файла, обсужденных в Разделе 8.9 [Дескрипторные Флаги], флаги состояния файла разделяются дублированными дескрипторами файла, следующими из одиночного открытия файла.

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

Несколько флагов состояния файла могут быть изменены, в любое время используя fcntl. Они включают O_APPEND и O_NONBLOCK.

Символы в этом разделе определены в заглавном файле 'fcntl.h'.

int F_GETFL (макрос)

Эта макрокоманда используется как аргумент команды fcntl, для чтения флагов состояния файла для файла открытого с описателем filedes.

Нормальное возвращаемое значение из fcntl с этой командой ­ неотрицательное число, которое может интерпретироваться как поразрядное ИЛИ индивидуальных флагов. Флаги закодированы так же, как аргумент флагов для open (см. Раздел 8.1 [Открытие и Закрытие Файлов]), но здесь значимы только режимы доступа файла и O_APPEND и O_NONBLOCK флаги. Так как режимы доступа файла - не одноразрядные значения, Вы можете маскировать другие биты в возвращенных флагах с O_ACCMODE, чтобы сравнить их.

В случае ошибки, fcntl возвращает -1. Следующие errno условия ошибки определены для этой команды:

EBADF filedes аргумент недопустим.

-Макрос: int F_SETFL

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

        fcntl (filedes, F_SETFL, new_flags)
Вы не можете изменять режим доступа для файла таким образом; то есть был ли дескриптор файла открыт для чтения или для записи. Вы можете только заменять флаги O_APPEND и O_NONBLOCK.

Нормальное возвращаемое значение из fcntl с этой командой ­ неопределенное значение отличное от -1, которое указывает ошибку. Условия ошибки - такие же как для команды F_GETFL.

Следующие макрокоманды определены для анализа и построения значений флага состояния файла:

  • O_APPEND - бит, который делает возможным конкатенирующий режим для файла. Если установлен, то все операции write пишут данные в конце файла, расширяя его, независимо от текущей файловой позиции.
  • O_NONBLOCK Бит, который делает возможным режим неблокирования для файла. Если этот бит установлен, запрос чтения в файле может возвращаться немедленно с состоянием отказа, если не имеется никакого немедленно доступного ввода кроме блокирования. Аналогично, запросы write могут также возвращаться немедленно с состоянием отказа, если вывод нельзя написать немедленно.
  • O_NDELAY Это - синоним для O_NONBLOCK, предусматривающий совместимость с BSD.
-Макрос: int O_ACCMODE

Эта макрокоманда замещает маску, которая может быть поразрядна AND со значением флага состояния файла, чтобы произвести значение, представляющее режим доступа файла. Режим будет O_RDONLY, O_WRONLY, или O_RDWR.

       O_RDONLY Открывает файл для чтения.
       O_WRONLY Открывает файл для записи.
       O_RDWR Открывает файл, и для чтения и для записи.
Если Вы хотите изменить флаги состояния файла, Вы должны получить текущие флаги с F_GETFL и изменять их значение. Вот функция, чтобы устанавливать или очистить флаг O_NONBLOCK без изменения любых других флагов:

                int
                set_nonblock_flag (int desc, int value)
                {
                        int oldflags = fcntl (desc, F_GETFL, 0);

                        if (oldflags < 0)
                                        return oldflags;

                        if (value != 0)
                                        oldflags |= O_NONBLOCK;
                        else
                                        oldflags &= ~O_NONBLOCK;
                        return fcntl (desc, F_SETFL, oldflags);
                }

8.11 Блокировки Файла

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

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

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

Функции read и write фактически не выясняет, имеются ли блокировки в данном месте.

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

Блокировки связаны с процессами. Процесс может иметь только один вид набора блокировок для каждого байта данного файла. Когда любой дескриптор файла для того файла закрыт процессом, все блокировки, которые обрабатывают связи с тем файлом, опущены, даже если блокировки были сделаны, используя другие описатели, которые остаются открытыми. Аналогично, блокировки опущены, когда процесс выходит, и не наследуются дочерними созданными процессами (см. Раздел 23.4 [Создание Процесса]).

При создании блокировки, используйте struct flock, чтобы определить какую блокировку и где Вы хотите выполнить. Этот тип данных и связанные макрокоманды для fcntl функции объявлен в заглавном файле 'fcntl.h'.

       struct flock  (тип данных)

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

                short int l_type
Определяет тип блокировки; один из F_RDLCK, F_WRLCK, или F_UNLCK.
                short int l_whence
Соответствует к аргументу whence для fseek или lseek, и определяет то, относительно чего задано смещение. Значение может быть одно из SEEK_SET, SEEK_CUR, или SEEK_END.
                off_t l_start
Определяет смещение начала области, к которой блокировка применяется, и дана в байтах относительно отметки, заданной элементом l_whence.
                off_t l_len
Определяет длину области, которая будет блокирована. Значение 0 обрабатывается особенно; это означает область, что простирается до конца файла.
                pid_t l_pid
Это поле - ID процесса (см. Раздел 23.2 [Понятия Создания Процесса]) процесса, содержащего блокировку. Оно задается вызовом fcntl с командой F_GETLK, но игнорируется при создании блокировки. -Макрос int F_GETLK

Эта макрокоманда используется как аргумент команды fcntl, для определения, что она должна получить информацию относительно блокировки. Эта команда требует третьего аргумента типа struct flock *, чтобы быть переданной к fcntl, так, чтобы форма обращения была:

        fcntl (filedes, F_GETLK, lockp)

Вы должны определить тип блокировки F_WRLCK, если Вы хотите выяснять относительно блокировок чтения и записи, или F_RDLCK, если Вы хотите выяснять относительно блокировок только записи.

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

Если никакая блокировка не применяется, единственное изменение для структуры lockp, должно модифицировать l_type в значение F_UNLCK.

Нормальное возвращаемое значение из fcntl с этой командой ­ неопределенное значение отличное от -1, которое зарезервировано, чтобы указать ошибку. Следующие errno условия ошибки определены для этой команды:

EBADF filedes аргумент недопустим.

EINVAL

Или lockp аргумент не определяет допустимую информацию блокировки, или файл, связанный с filedes не поддерживает блокировки.

-Макрос int F_SETLK

Эта макрокоманда используется как аргумент команды fcntl, для определения, что она должна установить или снять блокировку. Эта команда требует третьего аргумента типа struct flock *, так, чтобы форма обращения была:

        fcntl (filedes, F_SETLK, lockp)
Если процесс уже имеет блокировку на любой части области, старая блокировка на той части, заменяется на новую блокировку. Вы можете удалять блокировку, определяя тип блокировки F_UNLCK.

Если блокировка не может быть установлена, fcntl возвращается немедленно со значением -1. Эта функция не блокирует ожидание пока другие процессы снимут блокировки. Если fcntl преуспевает, она возвращает значение отличное от -1.

Следующие errno условия ошибки определены для этой функции:

EACCES

EAGAIN

Блокировка не может быть установлена, потому что это блокировано существующей блокировкой на файле.

Некоторые системы используют EAGAIN в этом случае, и другие системы используют EACCES; ваша программа должна обработать их одинаково, после F_SETLK.

EBADF

Также: filedes аргумент недопустим; Вы запросили блокировку чтения, но filedes - не открыт для чтения; или, Вы запросили блокировку записи, но filedes - не открыт для записи.

EINVAL

Или lockp аргумент определяет недопустимую информацию блокировки, или файл, связанный с filedes не поддерживает блокировки.

ENOLCK

Система работает без ресурсов блокировки файла; имеются уже слишком много блокировок файла.

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

int F_SETLKW (макрос)

Эта макрокоманда используется как аргумент команды fcntl, для определения, что она должна установить или снять блокировку. Это ­ тоже что команда F_SETLK, но заставляет процесс блокировать (или ждать) пока запрос не будет определен.

Эта команда требует третьего аргумента типа struct flock *, как для команды F_SETLK.

Fcntl возвращаемые значения и ошибки - те же что для команды F_SETLK, но эти дополнительные errno условия ошибки определены для этой команды:

EINTR

Функция была прервана сигналом, во время ожидания. См. Раздел 21.5 [Прерванные Примитивы].

EDEADLK

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

Следующие макрокоманды определены для использования как значения для l_type элемента структуры flock. Значения - константы integer.

F_RDLCK

Эта макрокоманда используется, чтобы установить блокировку чтения.

F_WRLCK

Эта макрокоманда используется, чтобы установить блокировку записи.

F_UNLCK

Эта макрокоманда используется, чтобы установить, что область разблокируется.

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

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

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

8.12 Управляемый прерываниями Ввод

Если Вы устанавливаете FASYNC флаг состояния на дескрипторе файла (см. Раздел 8.10 [Флаги Состояния Файла]), сигнал SIGIO посланы всякий раз, когда производится ввод или вывод, становится возможным на этом описателе файла. Процесс или группа процессов, может быть выбрана чтобы получить сигнал, используя команду F_SETOWN для fcntl функции. Если дескриптор файла является гнездом, он также, выбирает получателя сигналов SIGURG, которые возникают, когда внепоточные данные прибывает в это гнездо; см. Раздел 11.8.8 [Внепоточные Данные].

Если дескриптор файла соответствует терминалу, то SIGIO сигналы посылаются группе приоритетного процесса терминала. См. Главу 24 [Управление заданиями].

Символы в этом разделе определены в заглавном файле 'fcntl.h'.

-Макрос int F_GETOWN

Эта макрокоманда используется как аргумент команды fcntl, для определения того, что она должна получить информацию относительно процесса или группы процессов, которой посланы сигналы SIGIO. (Для терминала, это - фактически приоритетный ID группы процессов, которую Вы можете получать использованием tcgetpgrp; см. Раздел 24.7.3 [Функции Доступа Терминала].)

Возвращаемое значение интерпретируется как ID процесса; если оно отрицательно, то абсолютное значение - ID группы процессов.

Следующее errno условие ошибки определено для этой команды:

EBADF

Filedes аргумент недопустим.

int F_SETOWN (макрос)

       fcntl (filedes, F_SETOWN, pid)

Возвращаемое значение из fcntl с этой командой - -1 в случае ошибки и некоторое другого значения если обращение успешно. Следующие errno условия ошибки определены для этой команды:

EBADF

Filedes аргумент недопустим.

ESRCH

Не имеется никакого процесса или группы процессов, соответствующей pid.


Вперед Назад Содержание