Описанная выше реализация, позволяющая нескольким процессам вызывать функцию accept
на одном и том же прослушиваемом дескрипторе, возможна только для систем 4.4BSD, в которых функция accept
реализована внутри ядра. Ядра системы SVR4, в которых accept реализована как библиотечная функция, не допускают этого. В самом деле, если мы запустим сервер из предыдущего раздела, в котором имеется несколько дочерних процессов, в Solaris 2.5 (система SVR4), то вскоре после того, как клиенты начнут соединяться с сервером, вызов функции accept
в одном из дочерних процессов вызовет ошибку EPROTO
, что свидетельствует об ошибке протокола.
Причины возникновения этой проблемы с библиотечной версией функции accept в SVR4 связаны с реализацией потоков STREAMS и тем фактом, что библиотечная функция accept не является атомарной операцией. В Solaris 2.6 эта проблема решена, но в большинстве реализаций SVR4 она остается.
Решением этой проблемы является защита вызова функции accept
при помощи блокировки, так что в данный момент времени только один процесс может быть блокирован в вызове этой функции. Другие процессы также будут блокированы, так как они будут стремиться установить блокировку для вызова функции accept
.
Существует несколько способов реализации защиты вызова функции accept
, о которых рассказывается во втором томе[2] данной серии. В этом разделе мы используем блокировку файла функцией fcntl
согласно стандарту POSIX.
Единственным изменением в функции main
(см. листинг 30.6) будет добавление вызова функции my_lock_init
перед началом цикла, в котором создаются дочерние процессы:
+ my_lock_init("/tmp/lock.XXXXXX"); /* один файл для всех дочерних
процессов */
for (i = 0; i < nchildren; i++)
pids[i] = child_make(i, listenfd, addrlen); /* возвращение
родительского процесса */
Функция child_make
остается такой же, как в листинге 30.8. Единственным изменением функции child_main
(см. листинг 30.9) является блокирование перед вызовом функции accept
и снятие блокировки после завершения этой функции:
for (;;) {
clilen = addrlen;
+ my_lock_wait();
connfd = Accept(listenfd, cliaddr, &clilen);
+ my_lock_release();
web_child(connfd); /* обработка запроса */
Close(connfd);
}
В листинге 30.12 показана наша функция my_lock_init
, в которой используется блокировка файла согласно стандарту POSIX.
Листинг 30.12. Функция my_lock_init: блокировка файла
//server/lock_fcntl.c
1 #include "unp.h"
2 static struct flock lock_it, unlock_it;
3 static int lock_fd = -1;
4 /* fcntl() не выполнится, если не будет вызвана функция my_lock_init() */
5 void
6 my_lock_init(char *pathname)
7 {
8 char lock_file[1024];
9 /* копируем строку вызывающего процесса на случай, если это константа */
10 strncpy(lock_file, pathname, sizeof(lock_file));
11 lock_fd = Mkstemp(lock_file);
12 Unlink(lock_file); /* но lock_fd остается открытым */
13 lock_it.l_type = F_WRLCK;
14 lock_it.l_whence = SEEK_SET;
15 lock_it.l_start = 0;
16 lock_it.l_len = 0;
17 unlock_it.l_type = F_UNLCK;
18 unlock_it.l_whence = SEEK_SET;
19 unlock_it.l_start = 0;
20 unlock_it.l_len = 0;
21 }
Вильям Л Саймон , Вильям Саймон , Наталья Владимировна Макеева , Нора Робертс , Юрий Викторович Щербатых
Зарубежная компьютерная, околокомпьютерная литература / ОС и Сети, интернет / Короткие любовные романы / Психология / Прочая справочная литература / Образование и наука / Книги по IT / Словари и Энциклопедии