SELECT(2)

НАЗВАНИЕ
select, pselect, FD_CLR, FD_ISSET, FD_SET, FD_ZERO - синхронное мультиплексирование ввода-вывода

КРАТКАЯ СВОДКА
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

int pselect(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout, sigset_t * sigmask);

FD_CLR(int fd, fd_set *set);
FD_ISSET(int fd, fd_set *set);
FD_SET(int fd, fd_set *set);
FD_ZERO(fd_set *set);

ОПИСАНИЕ
Функции select и pselect ждут изменения статуса нескольких файловых дескрипторов.

Они почти идентичны, только

(i) select использует тайм-аут в виде struct timeval (с секундами и микросекундами), тогда как pselect использует struct timespec (с секундами и наносекундами).

(ii)
    Функция select может обновить параметр timeout, чтобы сообщить, сколько времени осталось. Функция pselect не изменяет этот параметр.

(iii)
    Функция select не содержит параметра sigmask, и ведет себя как pselect с параметром sigmask, равным NULL.

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

Для манипуляций наборами существуют четыре макроса: FD_ZERO очищает набор. FD_SET и FD_CLR добавляют или удаляют заданный дескриптор из набора. FD_ISSET проверяет, является ли дескриптор частью набора; этот макрос полезен после возврата из функции select.

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

timeout -- это верхняя граница времени, которое пройдет перед возвратом из select. Можно использовать ноль, при этом select завершится немедленно. (Это полезно для периодического опроса.) Если timeout равен NULL (нет тайм-аута), то select будет ожидать изменений неопределенное время.

sigmask -- это указатель на маску сигналов (см. sigprocmask(2)); если этот параметр не равен NULL, то pselect сначала замещает текущую маску сигналов на ту, на которую указывает sigmask, затем выполняет select, и восстанавливает исходную маску сигналов.

Идея pselect в том, что если нужно подождать события: сигнала или активности на файловом дескрипторе, то требуется атомарная проверка, чтобы предотвратить race condition. (Предположим, обработчик сигнала устанавливает глобальный флаг и возвращает управление. Тогда проверка этого глобального флага, за которой следует select(), может привести к подвисанию, если сигнал появляется сразу после проверки, но прямо перед вызовом select. С другой стороны, pselect позволяет сначала заблокировать сигналы, обработать пришедшие сигналы, а затем вызвать pselect() с желаемой sigmask, тем самым избегая race condition.) Так как Linux в настоящее время не содержит системного вызова pselect(), текущая реализация этой процедуры в glibc все еще содержит race condition.

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
При успешном завершении select и pselect возвращают количество дескрипторов, находящихся в наборах дескрипторов, причем это количество может быть равным нулю, если тайм-аут истекает, а интересующие нас события так и не произошли. При ошибке возвращается -1, а errno устанавливается должным образом; наборы дескрипторов и значение timeout становятся неопределены, поэтому при ошибке нельзя полагаться на их значение.

ОШИБКИ

EBADF
    В одном из наборов находится неверный файловый дескриптор.

EINTR
    Был пойман незаблокированный сигнал.

EINVAL
    n отрицательно.

ENOMEM
    Функция select не смогла выделить участок памяти для внутренних таблиц.

ЗАМЕЧАНИЕ
В некоторых программах select вызывается с тремя пустыми наборами файлов, n равным нулю, и ненулевым значением timeout, что является довольно переносимым способом сделать задержку с миллисекундной точностью.

Под Linux timeout изменяется, чтобы сообщить количество времени, которое не было использовано; большинство других реализаций не делают этого. Это приводит к проблемам как в коде под Linux, который читает значение timeout и переносится в другие операционные системы, так и когда код переносится под Linux и использует при этом struct timeval для нескольких функций select в цикле без повторной инициализации. Считайте, что параметр timeout неопределен после возврата из функции select.

ПРИМЕР
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int
main(void)
{
    fd_set rfds;
    struct timeval tv;
    int retval;
    /* Ждем, пока на стандартном вводе (fd 0) что-нибудь
       появится. */
    FD_ZERO(&rfds);
    FD_SET(0, &rfds);
    /* Ждем не больше пяти секунд. */
    tv.tv_sec = 5;
    tv.tv_usec = 0;
    retval = select(1, &rfds, NULL, NULL, &tv);
    /* Не полагаемся на значение tv! */
    if (retval)
        printf("Данные доступны.\n");
        /* Теперь FD_ISSET(0, &rfds) вернет истинное значение. */
    else
        printf("Данные не появились в течение пяти секунд.\n");
    exit(0);
}

СООТВЕТСТВИЕ СТАНДАРТАМ
4.4BSD (функция select впервые появилась в 4.2BSD). Обычно переносится с/на не-BSD системы, поддерживающие уровень BSD-сокетов (включая варианты System V). Однако заметьте, что варианты System V обычно устанавливают значение переменной timeout перед выходом, а вариант BSD -- нет.

Функция pselect определена в IEEE Std 1003.1g-2000 (POSIX.1g). Ее можно найти в glibc2.1 и позднее. Glibc2.0 содержит функцию с таким именем, но без параметра sigmask.

СМОТРИ ТАКЖЕ
accept(2), connect(2), poll(2), read(2), recv(2), send(2), sigprocmask(2), write(2)

ПЕРЕВОД
Copyright (C) Alexey Mahotkin <alexm@hsys.msk.ru> 2000-2001