Уильям Стивенс - UNIX: разработка сетевых приложений
- Название:UNIX: разработка сетевых приложений
- Автор:
- Жанр:
- Издательство:Питер
- Год:2007
- Город:Санкт-Петербург
- ISBN:5-94723-991-4
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Уильям Стивенс - UNIX: разработка сетевых приложений краткое содержание
Новое издание книги, посвященной созданию веб-серверов, клиент-серверных приложений или любого другого сетевого программного обеспечения в операционной системе UNIX, — классическое руководство по сетевым программным интерфейсам, в частности сокетам. Оно основано на трудах Уильяма Стивенса и полностью переработано и обновлено двумя ведущими экспертами по сетевому программированию. В книгу включено описание ключевых современных стандартов, реализаций и методов, она содержит большое количество иллюстрирующих примеров и может использоваться как учебник по программированию в сетях, так и в качестве справочника для опытных программистов.
UNIX: разработка сетевых приложений - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
В данном случае значение времени является абсолютным значением времени , в отличие от относительного значения разницы во времени ( time delta ) между некоторыми событиями. Иными словами, abstime
— это системное время, то есть количество секунд и наносекунд, прошедших с 1 января 1970 года (UTC) до того момента, когда эта функция должна вернуть управление. Здесь имеется различие как с функцией pselect
, так и с функцией select
, задающими количество секунд (и наносекунд в случае pselect
) до некоторого момента в будущем, когда функция должна вернуть управление. Обычно для этого вызывается функция gettimeofday
, которая выдает текущее время (в виде структуры timeval
), а затем оно копируется в структуру timespec
и к нему добавляется требуемое значение:
struct timeval tv;
struct timespec ts;
if (gettimeofday(&tv, NULL)
err_sys("gettimeofday error");
ts.tv_sec = tv.tv_sec + 5; /* 5 с в будущем */
ts.tv_nsec = tv.tv_usec * 1000; /* микросекунды переводим в наносекунды */
pthread_cond_timedwait( , &ts);
Преимущество использования абсолютного времени (в противоположность относительному) заключается в том, что функция может завершиться раньше (возможно, из-за перехваченного сигнала). Тогда функцию можно вызвать снова, не меняя содержимое структуры timespec
. Недостаток этого способа заключается в необходимости вызывать дополнительно функцию gettimeofday
перед тем, как в первый раз вызывать функцию pthread_cond_timedwait
.
В POSIX определена новая функция clock_gettime, возвращающая текущее время в виде структуры timespec.
26.9. Веб-клиент и одновременный доступ
Изменим код нашего веб-клиента из раздела 26.6: уберем вызов функции Solaris thr_join
и заменим его вызовом функции pthread_join
. Как сказано в разделе 26.6, теперь нам нужно точно указать, завершения какого потока мы ждем. Для этого мы используем условную переменную, описанную в разделе 26.8.
Единственным изменением в отношении глобальных переменных (см. листинг 26.7) является добавление нового флага и условной переменной:
#define F_JOINED 8 /* количество потоков */
int ndone; /* количество завершившихся потоков */
pthread_mutex_t ndone_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t ndone_cond = PTHREAD_COND_IINITIALIZER;
Единственным изменением функции do_get_read
(см. листинг 26.9) будет увеличение на единицу значения переменной ndone
и оповещение главного цикла о завершении выполнения потока:
printf("end-of-file on %s\n", fptr->f_name);
Close(fd);
Pthread_mutex_lock(&ndone_mutex);
fptr->f_flags = F_DONE; /* сбрасывает флаг F_READING */
ndone++;
Pthread_cond_signal(&ndone_cond);
Pthread_mutex_unlock(&ndone_mutex);
return(fptr); /* завершение выполнения потока */
}
Большинство изменений касается главного цикла, представленного в листинге 26.8. Новая версия показана в листинге 26.13.
Листинг 26.13. Основной рабочий цикл функции main
//threads/web03.c
43 while (nlefttoread > 0) {
44 while (nconn 0) {
45 /* находим файл для считывания */
46 for (i = 0; i
47 if (file[i].f_flags == 0)
48 break;
49 if (i == nfiles)
50 err_quit("nlefttoconn = %d but nothing found", nlefttoconn);
51 file[i].f_flags = F_CONNECTING;
52 Pthread_create(&tid, NULL, &do_get_read, &file[i]);
53 file[i].f_tid = tid;
54 nconn++;
55 nlefttoconn--;
56 }
57 /* Ждем завершения выполнения одного из потоков */
58 Pthread_mutex_lock(&ndone_mutex);
59 while (ndone == 0)
60 Pthread_cond_wait(&ndone_cond, &ndone_mutex);
61 for (i = 0; i
62 if (file[i].f_flags & F_DONE) {
63 Pthread_join(file[i].f_tid, (void**)&fptr);
64 if (&file[i] != fptr)
65 err_quit("file[i] != fptr");
66 fptr->f_flags = F_JOINED; /* clears F_DONE */
67 ndone--;
68 nconn--;
69 nlefttoread--;
70 printf("thread %d for %s done\n", fptr->f_tid, fptr->f_name);
71 }
72 }
73 Pthread_mutex_unlock(&ndone_mutex);
74 }
75 exit(0);
76 }
44-56
Эта часть кода не изменилась.
57-60
Мы ждем завершения выполнения потоков, отслеживая, когда значение ndone
станет равно нулю. Как сказано в разделе 26.8, эта проверка должна быть проведена перед тем, как взаимное исключение будет блокировано, а переход потока в состояние ожидания осуществляется функцией pthread_cond_wait
.
61-73
Когда выполнение потока завершилось, мы перебираем все структуры file
, отыскивая соответствующий поток, вызываем pthread_join
, а затем устанавливаем новый флаг F_JOINED
.
В табл. 16.1 показано, сколько времени требует выполнение этой версии веб-клиента, а также версии, использующей неблокируемую функцию connect
.
26.10. Резюме
Создание нового потока обычно требует меньше времени, чем порождение нового процесса с помощью функции fork
. Одно это уже является большим преимуществом использования потоков на активно работающих сетевых серверах. Многопоточное программирование, однако, представляет собой отдельную технологию, требующую большей аккуратности при использовании.
Все потоки одного процесса совместно используют глобальные переменные и дескрипторы, тем самым эта информация становится доступной всем потокам процесса. Но совместное использование информации вносит проблемы, связанные с синхронизацией доступа к разделяемым переменным, и поэтому нам следует использовать примитивы синхронизации технологии Pthreads — взаимные исключения и условные переменные. Синхронизация доступа к совместно используемым данным — необходимое условие почти для любого приложения, работающего с потоками.
При разработке функций, которые могут быть вызваны таким приложением, нужно учитывать требование безопасности в многопоточной среде. Это требование выполнимо при использовании собственных данных потоков (thread-specific data), пример которых мы показали при рассмотрении функции readline
в этой главе.
К модели потоков мы вернемся в главе 30, где сервер при запуске создает пул потоков. Для обслуживания очередного клиентского запроса используется любой свободный поток.
Упражнения
1. Сравните использование дескриптора в случае, когда в коде сервера применяется функция fork
, и в случае, когда используются потоки. Предполагается, что одновременно обслуживается 100 клиентов.
2. Что произойдет в листинге 26.2, если поток при завершении функции str_echo
не вызовет функцию close
для закрытия сокета?
3. В листингах 5.4 и 6.2 мы выводили сообщение Server terminated prematurely
(Сервер завершил работу преждевременно), когда мы ждали от сервера прибытия отраженной строки, а вместо этого получали признак конца файла (см. раздел 5.12). Модифицируйте листинг 26.1 таким образом, чтобы в соответствующих случаях также выдавалось аналогичное сообщение.
Интервал:
Закладка: