KnigaRead.com/
KnigaRead.com » Компьютеры и Интернет » Программное обеспечение » Уильям Стивенс - UNIX: разработка сетевых приложений

Уильям Стивенс - UNIX: разработка сетевых приложений

На нашем сайте KnigaRead.com Вы можете абсолютно бесплатно читать книгу онлайн Уильям Стивенс, "UNIX: разработка сетевых приложений" бесплатно, без регистрации.
Перейти на страницу:

3. Когда завершается дочерний процесс, родительский процесс уведомляется об этом с помощью сигнала SIGCHLD, и обработчик сигналов родительского процесса получает идентификатор завершающегося дочернего процесса. Он снова включает функцию select для соответствующего сокета, устанавливая бит для этого сокета в своем наборе дескрипторов.

Причина, по которой дейтаграммный сервер должен завладевать сокетом, пока он не завершит работу, лишая тем самым демон inetd возможности выполнять функцию select на этом сокете для проверки готовности его для чтения (в ожидании другой дейтаграммы клиента), в том, что для сервера дейтаграмм существует только один сокет, в отличие от сервера TCP, у которого имеется прослушиваемый сокет и по одному присоединенному сокету для каждого клиента. Если демон inetd не отключил чтение на сокете дейтаграмм и, допустим, родительский процесс (inetd) завершил выполнение перед дочерним, дейтаграмма от клиента все еще будет находиться в приемном буфере сокета. Это приводит к тому, что функция select снова сообщает, что сокет готов для чтения, и демон inetd снова выполняет функцию fork, порождая другой (ненужный) дочерний процесс. Демон inetd должен игнорировать дейтаграммный сокет до тех пор, пока он не узнает, что дочерний процесс прочитал дейтаграмму из приемного буфера сокета. Демон inetd узнает, что дочерний процесс закончил работу с сокетом, путем получения сигнала SIGCHLD, указывающего на то, что дочерний процесс завершился. Подобный пример мы показываем в разделе 22.7.

Пять стандартных служб Интернета, описанных в табл. 2.1, обеспечиваются самим демоном inetd (см. упражнение 13.2).

Поскольку функцию accept для сервера TCP вызывает демон inetd (а не сам сервер), реальный сервер, запускаемый демоном inetd, обычно вызывает функцию getpeername для получения IP-адреса и номера порта клиента. Вспомните рис. 4.9, где мы показывали, что после выполнения вызовов fork и exec (что выполняет демон inetd) у реального сервера есть единственный способ получить идентификацию клиента — вызвать функцию getpeername.

Демон inetd обычно не используется для серверов, работающих с большими объемами данных, в особенности почтовыми серверами и веб-серверами. Например, функция sendmail обычно запускается как стандартный параллельный сервер, как мы отмечали в разделе 4.8. В этом режиме стоимость порождения процесса для каждого клиентского соединения равна стоимости функции fork, тогда как в случае сервера TCP, активизированного демоном inetd, — стоимости функций fork и exec. Веб-серверы используют множество технологий для минимизации накладных расходов при порождении процессов для обслуживания клиентов, как мы покажем в главе 30.

13.6. Функция daemon_inetd

В листинге 13.3 показана функция daemon_inetd, которую мы можем вызвать с сервера, запущенного демоном inetd.

Листинг 13.3. Функция daemon_inetd для придания свойств демона процессу, запущенному демоном inetd

//daemon_inetd.c

1 #include "unp.h"

2 #include <syslog.h>


3 extern int daemon_proc; /* определено в error.c */


4 void

5 daemon_inetd(const char *pname, int facility)

6 {

7  daemon_proc = 1; /* для наших функций err_XXX() */

8  openlog(pname, LOG_PID, facility);

9 }

Эта функция тривиальна по сравнению с daemon_init, потому что все шаги выполняются демоном inetd при запуске. Все, что мы делаем, — устанавливаем флаг daemon_proc для наших функций ошибок (см. табл. Г.1) и вызываем функцию openlog с теми же аргументами, что и при вызове функции daemon_init, представленной в листинге 13.1.

Пример: сервер времени и даты, активизированный демоном inetd

Листинг 13.4 представляет собой модификацию нашего сервера времени и даты, показанного в листинге 13.2, который может быть активизирован демоном inetd.

Листинг 13.4. Не зависящий от протокола сервер времени и даты, который может быть активизирован демоном inetd

//inetd/daytimetcpsrv3.c

 1 #include "unp.h"

 2 #include <time.h>


 3 int

 4 main(int argc, char **argv)

 5 {

 6  socklen_t len;

 7  struct sockaddr *cliaddr;

 8  char buff[MAXLINE];

 9  time_t ticks;


10  daemon_inetd(argv[0], 0);


11  cliaddr = Malloc(MAXSOCKADDR);

12  len = MAXSOCKADDR;

13  Getpeername(0, cliaddr, &len);

14  err_msg("connection from %s", Sock_ntop(cliaddr, len));


15  ticks = time(NULL);

16  snprintf(buff, sizeof(buff), "%.24srn", ctime(&ticks));

17  Write(0, buff, strlen(buff));


18  Close(0); /* закрываем соединение TCP */

19  exit(0);

20 }

В программе сделано два важных изменения. Во-первых, исчез весь код создания сокета: вызовы функций tcp_listen и accept. Эти шаги выполняются демоном inetd, и мы ссылаемся на соединение TCP, используя нулевой дескриптор (стандартный поток ввода). Во-вторых, исчез бесконечный цикл for, поскольку сервер активизируется по одному разу для каждого клиентского соединения. После предоставления сервиса клиенту сервер завершает свою работу.

Вызов функции getpeername

11-14 Поскольку мы не вызываем функцию tcp_listen, мы не знаем размера структуры адреса сокета, которую она возвращает, а поскольку мы не вызываем функцию accept, то не знаем и адреса протокола клиента. Следовательно, мы выделяем буфер для структуры адреса сокета, используя нашу константу MAXSOCKADDR и вызываем функцию getpeername с нулевым дескриптором в качестве первого аргумента.

Чтобы выполнить этот пример в нашей системе Solaris, сначала мы присваиваем службе имя и порт, добавляя следующую строку в /etc/services:

mydaytime 9999/tcp

Затем добавляем строку в /etc/inetd.conf:

mydaytime stream tcp nowait andy

/home/andy/daytimetcpsrv3 daytimetcpsrv3

(Мы разбили длинную строку на более короткие.) Мы помещаем выполняемый код в заданный файл и отправляем демону inetd сигнал SIGHUP, сообщающий ему, что нужно заново считать файл конфигурации. Следующий шаг — выполнить программу netstat, чтобы проверить, что на порте TCP 9999 создан прослушиваемый сокет:

solaris % netstat -na | grep 9999

*.9999 *.* 0 0 49152 0 LISTEN

Затем мы запускаем сервер с другого узла:

linux % telnet solaris 9999

Trying 192.168.1.20...

Connected to solaris.

Escape character is '^]'.

Tue Jun 10 11:04:02 2003

Connection closed by foreign host.

Файл /var/amd/messages (в который, как указано в нашем файле /etc/syslog.conf, должны направляться наши сообщения с аргументом facility=LOG_USER) содержит запись:

Jun 10 11:04:02 solaris daytimetcpsrv3[28724]: connection from 192.168.1.10.58145

13.7. Резюме

Демоны — это процессы, выполняемые в фоновом режиме независимо от управления с терминалов. Многие сетевые серверы работают как демоны. Все выходные данные демона обычно отправляются демону syslogd при помощи вызова функции syslog. Администратор полностью контролирует все, что происходит с этими сообщениями, основываясь на том, какой демон отправил данное сообщение и насколько оно серьезно.

Чтобы запустить произвольную программу и выполнять ее в качестве демона, требуется пройти несколько шагов: вызвать функцию fork для запуска в фоновом режиме, вызвать функцию setsid для того, чтобы создать новый сеанс POSIX и стать главным процессом сеанса, снова вызвать функцию fork, чтобы избежать перехода в режим управления с терминала, изменить рабочий каталог и маску режима создания файла и закрыть все ненужные файлы. Наша функция daemon_init выполняет все эти шаги.

Многие серверы Unix запускаются демоном inetd. Он осуществляет все необходимые шаги по превращению процесса в демон, и при запуске действительного сервера открывается сокет для стандартных потоков ввода, вывода и сообщений об ошибках. Это позволяет нам опустить вызовы функций socket, bind, listen и accept, поскольку все эти шаги выполняются демоном inetd.

Упражнения

1. Что произойдет в листинге 13.2, если мы отложим вызов функции daemon_init до завершения обработки аргументов командной строки и функция err_quit будет вызвана до того, как программа станет демоном?

2. Как вы думаете, какие из 10 серверов, перечисленных в табл. 2.1 (учитываются версии TCP и UDP для каждой из пяти служб, управляемых демоном inetd), реализуются с помощью вызова функции fork, а какие не требуют этой функции?

3. Что произойдет, если мы создадим сокет UDP, свяжем порт 7 с сокетом (стандартный эхо-сервер в табл. 2.1) и отправим дейтаграмму UDP-серверу chargen?

Перейти на страницу:
Прокомментировать
Подтвердите что вы не робот:*