UNIX: разработка сетевых приложений
Шрифт:
Сообщения для записи в журнал могут также генерироваться командой
logger
. Это может использоваться в сценариях интерпретатора команд, например для отправки сообщений демону syslogd
. 13.4. Функция daemon_init
В листинге 13.1 [1] показана функция, называемая
daemon_init
, которую мы можем вызвать (обычно с сервера), чтобы придать процессу свойства демона.1
Все исходные коды программ, опубликованные в этой книге, вы можете найти по адресу http://www.piter.com.
Листинг 13.1.
//daemon _init.с
1 #include "unp.h"
2 #include <syslog.h>
3 #define MAXFD 64
4 extern int daemon_proc; /* определен в error.с */
5 int
6 daemon_init(const char *pname, int facility)
7 {
8 int i;
9 pid_t pid;
10 if ((pid = Fork) < 0)
11 return (-1);
12 else if (pid)
13 _exit(0); /* родитель завершается */
14 /* 1-й дочерний процесс продолжает работу... */
15 if (setsid < 0) /* становится главным процессом сеанса */
16 return (-1);
17 Signal(SIGHUP, SIG_IGN);
18 if ((pid = Fork) < 0)
19 return (-1);
20 else if (pid)
21 _exit(0); /* 1-й дочерний процесс завершается */
22 /* 2-й дочерний процесс продолжает работу */
23 daemon_proc = 1; /* для функций err_XXX */
24 chdir("/"); /* смена текущего каталога */
25 /* закрытие дескрипторов файлов*/
26 for (i = 0; i < MAXFD; i++)
27 close(i);
28 /* перенаправление stdin, stdout и stderr в /dev/null */
29 open("/dev/null", O_RDONLY);
30 open("/dev/null", O_RDWR);
31 open("/dev/null", O_RDWR);
32 openlog(pname, LOG_PID, facility);
33 return (0); /* успешное завершение */
34 }
Вызов функции fork
10-13
Сначала мы вызываем функцию fork
, после чего родительский процесс завершается, а дочерний продолжается. Если процесс был запущен из интерпретатора команд в фоновом режиме, то, когда родительский процесс завершается, оболочка считает, что команда выполнена. Это автоматически запускает дочерний процесс в фоновом режиме. Дочерний процесс наследует идентификатор группы процессов от родительского процесса, но получает свой собственный идентификатор процесса. Это гарантирует, что дочерний процесс не является главным в группе процессов, что требуется для следующего вызова функции setsid
. Вызов функции setsid
15-16
Функция setsid
— это функция POSIX, создающая новый сеанс. (В главе 9 [110] подробно рассказывается о взаимоотношениях процессов.) Процесс становится главным в новом сеансе, становится главным в новой группе процессов и не имеет управляющего терминала. Игнорирование сигнала SIGHUP и новый вызов функции fork
17-21
Мы игнорируем сигнал SIGHUP
и
снова вызываем функцию fork
. Когда эта функция завершается, родительский процесс на самом деле является первым дочерним процессом, и он завершается, оставляя выполняться второй дочерний процесс. Назначение второй функции fork
— гарантировать, что демон не сможет автоматически получить управляющий терминал, если потом он откроет устройство терминала. В SVR4, когда главный процесс сеанса без управляющего терминала открывает устройство терминала (которое в этот момент не является управляющим терминалом для другого сеанса), терминал становится управляющим терминалом главного процесса сеанса. Но вызывая второй раз функцию fork
, мы гарантируем, что второй дочерний процесс больше не является главным в сеансе, поэтому он не может получить управляющий терминал. Сигнал SIGHUP
приходится игнорировать, поскольку, когда главный процесс сеанса завершает работу (первый дочерний процесс), всем процессам в сеансе (нашему второму дочернему процессу) посылается сигнал SIGHUP. Установка флага для функций ошибок
23
Мы присваиваем глобальной переменной daemon_proc
ненулевое значение. Эта внешняя переменная задается нашими функциями err_ XXX
(см. раздел Г.4), и ее ненулевое значение сообщает этим функциям, что нужно вызвать функцию syslog
вместо функции fprintf
(которая выводит сообщение об ошибке в стандартный поток сообщений об ошибках). Это спасает нас от необходимости проходить через весь наш код и вызывать одну из наших функций ошибок, если сервер не работает как демон (то есть когда мы проверяем сервер), а при работе в режиме демона заменять все вызовы на вызовы syslog
. Изменение рабочего каталога и сброс всех битов в маске режима создания файла
24
Мы изменяем рабочий каталог на корневой каталог, хотя у некоторых демонов могут быть причины изменить рабочий каталог на какой-либо другой. Например, демон печати может изменить его на каталог, в котором накапливается содержимое заданий для принтера и происходит вся работа по выводу данных на печать. Если демоном сбрасывается дамп (файл core
), он появляется в текущем рабочем каталоге. Другой причиной для изменения рабочего каталога является то, что демон мог быть запущен в любой файловой системе, и если он там останется, эту систему нельзя будет размонтировать, во всяком случае, без жестких мер. Закрытие всех открытых дескрипторов
25-27
Мы закрываем все открытые дескрипторы, которые наследуются от процесса, запустившего демон (обычно этим процессом бывает интерпретатор команд). Проблема состоит в определении наибольшего используемого дескриптора: в Unix нет ни одной функции, предоставляющей это значение. Есть способы определения максимального числа дескрипторов, которое может открыть процесс, но даже это достаточно сложно [110, с. 43], поскольку предел может быть бесконечным. Наше решение — закрыть первые 64 дескриптора, даже если большинство из них, возможно, не было открыто. ПРИМЕЧАНИЕ
Solaris предоставляет функцию closefrom, позволяющую демонам решать эту проблему.
Перенаправление stdin, stdout и stderr в /dev/null
29-31
Некоторые демоны открывают /dev/null
для чтения и записи и подключают к нему дескрипторы стандартных потоков ввода, вывода и сообщений об ошибках. Это гарантирует, что наиболее типичные дескрипторы открыты и операция чтения из любого из них возвращает 0 (конец файла), а ядро игнорирует все, что записано в любой из этих трех дескрипторов. Причина, по которой требуется открыть эти дескрипторы, заключается в том, что любая библиотечная функция, вызываемая демоном и считающая, что она может читать из стандартного потока ввода или записывать либо в стандартный поток вывода, либо в стандартный поток сообщений об ошибках, не должна завершиться с ошибкой. Отказ был бы потенциально опасен: если демон открывает сокет для связи с клиентом, дескриптор сокета воспринимается как стандартный поток вывода, поэтому ошибочный вызов какой-нибудь функции типа perror
может привести к отправке клиенту нежелательных данных.
Поделиться с друзьями: