В качестве результата функция select() возвращает одно из следующих значений.
• -1 свидетельствует об ошибке: EBADF или EINTR. Первая говорит о том, что один из файловых дескрипторов в группе readfds, writefds или exceptfds является недействительным (например, неоткрытым). Вторая сигнализирует о прерывании вызова обработчиком сигнала (как отмечалось в разделе 21.5, вызов select() в этой ситуации никогда не перезапускается автоматически).
• 0 означает, что время ожидания вызова истекло до того, как один из файловых дескрипторов стал готовым. В таком случае все возвращаемые наборы будут пустыми.
• Положительное значение говорит о готовности одного или нескольких файловых дескрипторов и соответствует их количеству. В данной ситуации следует проверить каждый возвращаемый набор (используя макрос FD_ISSET()), чтобы узнать, какие события ввода/вывода имели место. Если один и тот же дескриптор входит сразу в несколько наборов, то учитывается в каждом из них и считается готовым к более чем одному событию. Иными словами, вызов select() возвращает общее количество файловых дескрипторов, помеченных как готовые во всех трех наборах.
Пример программы
Применение вызова select() демонстрируется на примере листинга 59.1. С помощью аргументов командной строки можно указать время ожидания и файловые дескрипторы, которые мы хотим отслеживать. Первый параметр соответствует аргументу timeout вызова select() (в секундах). Если указать знак минус (—), то аргументу timeout будет передано значение NULL, что приведет к перманентной блокировке. Каждый следующий аргумент командной строки обозначает номер файлового дескриптора, за которым нужно наблюдать; за ним идут буквы, описывающие проверяемые операции. Букв может быть только две: r (готовность к чтению) и w (готовность к записи).
Листинг 59.1. Мониторинг нескольких файловых дескрипторов с помощью вызова select()
altio/t_select.c
#include
#include
#include "tlpi_hdr.h"
static void
usageError(const char *progName)
{
fprintf(stderr, "Usage: %s {timeout|-} fd-num[rw]…\n", progName);
fprintf(stderr, " — means infinite timeout; \n");
fprintf(stderr, " r = monitor for read\n");
fprintf(stderr, " w = monitor for write\n\n");
fprintf(stderr, " e.g.: %s — 0rw 1w\n", progName);
exit(EXIT_FAILURE);
}
int
main(int argc, char *argv[])
{
fd_set readfds, writefds;
int ready, nfds, fd, numRead, j;
struct timeval timeout;
struct timeval *pto;
char buf[10]; /* Достаточно большой для хранения "rw\0" */
if (argc < 2 || strcmp(argv[1], "-help") == 0)
usageError(argv[0]);
/* Время ожидания для select() указывается в argv[1] */
if (strcmp(argv[1], "-") == 0) {
pto = NULL; /* Бесконечное время ожидания */
} else {
pto = &timeout
timeout.tv_sec = getLong(argv[1], 0, "timeout");
timeout.tv_usec = 0; /* Без микросекунд */
}
/* Обрабатываем остальные аргументы, чтобы сформировать
наборы файловых дескрипторов */
nfds = 0;
FD_ZERO(&readfds);
FD_ZERO(&writefds);
for (j = 2; j < argc; j++) {
numRead = sscanf(argv[j], "%d%2[rw]", &fd, buf);
if (numRead!= 2)
usageError(argv[0]);
if (fd >= FD_SETSIZE)
cmdLineErr("file descriptor exceeds limit (%d)\n", FD_SETSIZE);
if (fd >= nfds)
nfds = fd + 1; /* Записываем максимум fd +1 */
if (strchr(buf, 'r')!= NULL)
FD_SET(fd, &readfds);
if (strchr(buf, 'w')!= NULL)
FD_SET(fd, &writefds);
}
/* Все аргументы сформированы; теперь вызываем select() */
ready = select(nfds, &readfds, &writefds, NULL, pto);
/* Игнорируем исключительные события */
if (ready == -1)
errExit("select");
/* Выводим результат выполнения select() */
printf("ready = %d\n", ready);
for (fd = 0; fd < nfds; fd++)
printf("%d: %s%s\n", fd, FD_ISSET(fd, &readfds)? "r": "",
FD_ISSET(fd, &writefds)? "w": "");
if (pto!= NULL)
printf("timeout after select(): %ld.%03ld\n",
(long) timeout.tv_sec, (long) timeout.tv_usec / 1000);
exit(EXIT_SUCCESS);
}
altio/t_select.c
Использование программы из листинга 59.1 демонстрируется в следующей сессии командной строки. В первом примере мы делаем запрос на мониторинг ввода в файловом дескрипторе 0 со временем ожидания 10 секунд:
$ ./t_select 10 0r
ready = 1
0: r
timeout after select(): 8.003
$
Как видите, вызов select() определил, что готовым к чтению был один файловый дескриптор с номером 0. Мы также видим обновление значения аргумента timeout. Последняя строчка вывода содержит только приглашение командной строки; оно выводится, так как программа t_select не прочитала символ новой строки, сделавший файловый дескриптор 0 готовым. Следовательно, он был прочитан командной оболочкой, которая в ответ вывела еще одно приглашение.
В следующем примере мы опять следим за вводом в файловом дескрипторе 0, но на этот раз время ожидания равно 0:
$ ./t_select 0 0r