Чтение онлайн

ЖАНРЫ

UNIX: разработка сетевых приложений
Шрифт:

Сообщения для записи в журнал могут также генерироваться командой

logger
. Это может использоваться в сценариях интерпретатора команд, например для отправки сообщений демону
syslogd
.

13.4. Функция daemon_init

В листинге 13.1 [1] показана функция, называемая

daemon_init
, которую мы можем вызвать (обычно с сервера), чтобы придать процессу свойства демона.

1

Все исходные коды программ, опубликованные в этой книге, вы можете найти по адресу http://www.piter.com.

Листинг 13.1.

Функция daemon_init: придание процессу свойств демона

//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
может привести к отправке клиенту нежелательных данных.

Поделиться с друзьями: