UNIX: разработка сетевых приложений
ПРИМЕЧАНИЕТаблица и приведенные строки — это только пример. Большинство производителей добавили демону inetd свои собственные функции. Примером может служить возможность обрабатывать серверы вызовов удаленных процедур (RPC) в дополнение к серверам TCP и UDP, а также возможность обрабатывать другие протоколы, отличные от TCP и UDP. Полное имя для функции exec и аргументы командной строки сервера, очевидно, зависят от приложения.
Флаг wait-flag может быть достаточно труден для понимания. Он указывает, собирается ли демон, запускаемый inetd, взять на себя работу с прослушиваемым сокетом. Сервисы UDP лишены деления на прослушиваемые и принятые сокеты, и потому практически всегда создаются с флагом wait-flag, равным wait. Сервисы TCP могут вести себя по-разному, но чаще всего для них указывается флаг wait-flag со значением nowait.
Взаимодействие IPv6 с файлом /etc/inetd.conf зависит от производителя. Иногда в качестве поля protocol указывается tcp6 или udp6, чтобы подчеркнуть, что для сервера должен быть создан сокет IPv6. Некоторые разрешают использовать значения protocol, равные tcp46 и udp46, если сервер готов принимать соединения по обоим протоколам. Специальные названия протоколов обычно не включаются в файл /etc/protocols.
Иллюстрация действий, выполняемых демоном
, представлена на рис. 13.1.inetdРис. 13.1. Действия, выполняемые демоном inetd
1. При запуске демон читает файл
и создает сокет соответствующего типа (потоковый или дейтаграммный сокет) для всех служб, заданных в файле. Максимальное число серверов, которые может обрабатывать демон/etc/inetd.conf, зависит от максимального числа дескрипторов, которые он может создать. Каждый новый сокет добавляется к набору дескрипторов, который будет использован при вызове функцииinetd.select2. Для каждого сокета вызывается функция
, задающая заранее известный порт для сервера и универсальный IP-адрес. Этот номер порта TCP или UDP получается при вызове функцииbindс полямиgetservbyname-name иserviceиз файла конфигурации в качестве аргументов.protocol3. Для сокетов TCP вызывается функция
, так что принимаются входящие запросы на соединение. Этот шаг не выполняется для дейтаграммных сокетов.listen4. После того как созданы все сокеты, вызывается функция
, ожидающая, когда какой-либо из сокетов станет готов для чтения. Вспомните (раздел 6.3), что прослушиваемый сокет TCP становится готов для чтения, когда новое соединение готово быть принятым с помощью функцииselect, а сокет UDP становится готов для чтения, когда приходит дейтаграмма. Демонacceptбольшую часть времени блокирован в вызове функцииinetd, ожидая, когда сокет станет готов для чтения.select5. При указании флага
для сокетов TCP вызывается функцияnowaitсразу же, как только дескриптор сокета становится готов для чтения.accept6. Демон
запускает функциюinetd, и дочерний процесс обрабатывает запрос клиента. Это аналогично стандартному параллельному серверу (см. раздел 4.8).forkДочерний процесс закрывает все дескрипторы, кроме дескриптора, который он обрабатывает: новый присоединенный сокет, возвращаемый функцией
для сервера TCP, или исходный сокет UDP. Дочерний процесс трижды вызывает функциюaccept, подключая сокет к дескрипторам 0, 1 и 2 (стандартные потоки ввода, вывода и сообщений об ошибках). Исходный дескриптор сокета затем закрывается. При этом в дочернем процессе открытыми остаются только дескрипторы 0, 1 и 2. Если дочерний процесс читает из стандартного потока ввода, он читает из сокета, и все, что он записывает в стандартный поток вывода или стандартный поток сообщений об ошибках, записывается в сокет. Дочерний процесс вызывает функциюdup2, чтобы получить значение поляgetpwnam, заданного в файле конфигурации. Если это не поле root, дочерний процесс становится указанным пользователем при помощи функцийlogin-nameиsetgid. (Поскольку процессsetuidвыполняется с идентификатором пользователя, равным 0, дочерний процесс наследует этот идентификатор пользователя при выполнении функцииinetd, поэтому он имеет возможность стать любым пользователем по своему выбору.)forkТеперь дочерний процесс вызывает функцию
, чтобы выполнить соответствующую программу сервера (полеexec) для обработки запроса, передавая аргументы, указанные в файле конфигурации.server-program7. Если сокет является потоковым сокетом, родительский процесс должен закрыть присоединенный сокет (как наш стандартный параллельный сервер). Родительский процесс снова вызывает функцию
, ожидая, когда следующий сокет станет готов для чтения.selectЧтобы рассмотреть более подробно, что происходит с дескрипторами, на рис. 13.2 показаны дескрипторы демона
в момент прихода нового запроса на соединение от клиента FTP.inetdРис. 13.2. Дескрипторы демона inetd в тот момент, когда приходит запрос на порт 21 TCP
Запрос на соединение направляется на порт 21 TCP; новый присоединенный сокет создается функцией
.acceptНа рис. 13.3 показаны дескрипторы в дочернем процессе после вызова функции
, после того как дочерний процесс закрывает все остальные дескрипторы, кроме дескрипторов присоединенного сокета.forkРис. 13.3. Дескрипторы демона inetd в дочернем процессе
Следующий шаг для дочернего процесса — подключение присоединенного сокета к дескрипторам 0, 1 и 2 и последующее закрытие присоединенного сокета. При этом мы получаем дескрипторы, изображенные на рис. 13.4.
Рис. 13.4. Дескрипторы демона inetd после выполнения функции dup2
Затем дочерний процесс вызывает функцию
, и, как сказано в разделе 4.7, во время выполнения функцииexecвсе дескрипторы обычно остаются открытыми, поэтому реальный сервер, на котором выполняется функцияexec, использует любой из дескрипторов 0, 1 и 2 для взаимодействия с клиентом. Эти дескрипторы должны быть единственными открытыми на стороне сервера дескрипторами.execОписанный нами сценарий относится к ситуации, при которой файл конфигурации задает в поле
значениеwait-flagдля сервера. Это типично для всех служб TCP и означает, что демонуnowaitне нужно ждать завершения его дочернего процесса, перед тем как он примет другое соединение для данной службы. Если приходит другой запрос на соединение для той же службы, он возвращается родительскому процессу, как только тот снова вызовет функциюinetd. Шаги 4, 5 и 6, перечисленные выше, выполняются снова, и новый запрос обрабатывается другим дочерним процессом.select



