Разработка приложений в среде Linux. Второе издание
Шрифт:
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
. 3. Поскольку ядро переписывает структуры данных, передаваемые
select
, приложение вынуждено сбрасывать эти структуры каждый раз перед вызовом select
.
С poll
результаты ядра ограничены элементом revents
, что устраняет потребность в восстановлении структур данных после каждого вызова. 4. Использование структуры, основанной на множествах (например,
fd_set
) не масштабируется по мере увеличения количества доступных процессу файловых дескрипторов. Поскольку ее размер статичен, а не динамичен (обратите внимание на отсутствие соответствующего макроса, например, FD_FREE
), она не может расширяться или сжиматься в соответствии с потребностями приложения (или возможностями ядра). В Linux максимальный файловый дескриптор, который можно установить в fd_set
, равен 1023. Если понадобится больший файловый дескриптор, select
работать не будет. Единственным преимуществом
select
перед poll
является лучшая переносимость в старые системы. Поскольку небольшое количество таких реализаций все еще используется, следует применять select
, прежде всего, для понимания и эксплуатации существующих кодовых баз. Следующая короткая программа, подсчитывающая количество системных вызовов в секунду, демонстрирует, насколько
poll
эффективнее select
. 1: /* select-vs-poll.с */
2:
3: #include <fcntl.h>
4: #include <stdio.h>
5: #include <sys/poll.h>
6: #include <sys/select.h>
7: #include <sys/signal.h>
8: #include <unistd.h>
9:
10: int gotAlarm;
11:
12: void catch(int sig) {
13: gotAlarm = 1;
14: }
15:
16: #define HIGH_FD 1000
17:
18: int main(int argc, const char ** argv) {
19: int devZero;
20: int count;
21: fd_set select Fds;
22: struct pollfd pollFds;
23:
24: devZero = open("/dev/zero", O_RDONLY);
25: dup2(devZero, HIGH_FD);
26:
27: /* с помощью signal выяснить, когда время истекло */
28: signal(SIGALRM, catch);
29:
30: gotAlarm =0;
31: count = 0;
32: alarm(1);
33: while (!gotAlarm) {
34: FD_ZERO(&selectFds);
35: FD_SET(HIGH_FD, &selectFds);
36:
37: select(HIGH_FD + 1, &selectFds, NULL, NULL, NULL);
Поделиться с друзьями: