НАЗВАНИЕ
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 [AT] hsys.ru> 2000-2001