Еще одним параметром, связанным с переносимостью, является timeout
. Ядра Linux [77] Кроме некоторых экспериментальных ядер серии 2.1.
обновляют его, чтобы отобразить количество времени, оставшегося до тайм-аута вызова select()
, но большинство других систем Unix его не обновляют [78] Когда Линус Торвальдс впервые реализовал select() , неспособность ядра BSD обновлять timeout была отмечена как ошибка на man-странице для select() . Вместо написания ошибочного кода Линус решил "исправить" эту ошибку. К сожалению, комитеты по стандартам одобрили поведение BSD.
. Однако другие системы не обновляют тайм-аут с целью соответствия более привычной реализации.
Для переносимости устраните зависимость от поведения и явно настройте структуру timeout
перед вызовом select()
.
Теперь рассмотрим несколько примеров применения select()
. Для начала используем select()
без связи с файлами, создав вторичный вызов sleep()
.
#include
#include
int usecsleep(int usees) {
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = useсs;
return select(0, NULL, NULL, NULL, &tv);
}
Этот код разрешает переносимые паузы длительностью менее секунды (это обеспечивает также библиотечная функция BSD usleep()
, но select()
намного более переносима). Например, usecsleep(500000)
вызывает паузу минимум на полсекунды.
Вызов select()
также используется для решения примера мультиплексирования каналов, с которым мы работали. Решение очень похоже на решение при использовании poll()
.
1: /* mpx-select.c */
2:
3: #include
4: #include
5: #include
6: #include
7:
8: int main(void) {
9: int fds[2];
10: char buf[4096];
11: int i, rc, maxfd;
12: fd_set watchset; /* fds для чтения */
13: fd_set inset; /* обновляется select() */
14:
15: /* открыть оба канала */
16: if ((fds[0] = open("p1", O_RDONLY | O_NONBLOCK)) < 0) {
17: perror("open p1");
18: return 1;
19: }
20:
21: if ((fds[1] = open("p2", O_RDONLY | O_NONBLOCK)) < 0) {
22: perror("open p2");
23: return 1;
24: }
25:
26: /* начать чтение из обоих файловых дескрипторов */
27: FD_ZERO(&watchset);
28: FD_SET(fds[0], &watchset);
29: FD_SET(fds[1], &watchset);
30:
31: /* найти максимальный файловый дескриптор */
32: maxfd = fds[0] > fds[1] ? fds[0] : fds[1];
33:
34: /* пока наблюдаем за одним из fds[0] или fds[1] */
35: while (FD_ISSET(fds[0], &watchset) ||
36: FD_ISSET(fds[1], &watchset)) {
37: /* здесь копируем watchset, потому что select() обновляет его */
38: inset = watchset;
39: if (select(maxfd + 1, &inset, NULL, NULL, NULL) < 0) {
40: perror("select");
41: return 1;
42: }
43:
44: /* проверить, какой из файловых дескрипторов
45: готов для чтения из него */
46: for (i = 0; i < 2; i++) {
47: if (FD_ISSET(fds[i], &inset )) {
48: /* fds[i] готов для чтения, двигаться дальше... */
49: rc = read(fds[i], buf, sizeof (buf) - 1);
50: if (rc < 0) {
51: perror("read");
52: return 1;
53: } else if (!rc) {
54: /* этот канал закрыт, не пытаться
55: читать из него снова */
56: close(fds[i]);
57: FD_CLR(fds[i], &watchset);
58: } else {
59: buf[rc] = '\0';
60: printf("чтение: %s", buf);
61: }
62: }
63: }
64: }
65:
66: return 0;
67: }
13.1.4. Сравнение poll()
и select()
Обладая одинаковой функциональностью, poll()
и select()
также имеют существенные отличия. Наиболее очевидным отличием является тайм-аут, поддерживающий миллисекундную точность для poll()
и микросекундную точность для select()
. В действительности же это отличие почти незначительно, поскольку ни один системный вызов не будет подготовлен с точностью до микросекунды.
Более важное отличие связано с производительностью. Интерфейс poll()
обладает несколькими свойствами, делающими его намного эффективнее, чем select()
.
1. При использовании select()
ядру необходимо проверить все файловые дескрипторы между 0
и numfds - 1
, чтобы убедиться, заинтересовано ли приложение в событиях ввода-вывода для этого файлового дескриптора. Для приложений с большим количеством открытых файлов это может привести к существенным затратам, поскольку ядро проверяет, какие именно файловые дескрипторы являются объектом интереса.
2. Набор файловых дескрипторов передается ядру как битовая карта для select()
и как список для poll()
. Сложные битовые операции, необходимые для проверки и установки структур данных fd_set
, менее эффективны, чем простые проверки, требуемые для struct pollfd
.
Читать дальше