Процесс-демон. Определение и свойства
Процесс-де́мон — процесс специального назначения, создаваемый и управляемый системой. В отличие от других процессов является долгоживущим и запускается в фоновом режиме. Не имеет связанного с ним терминала для ввода и вывода.
Примерами служат syslogd, который записывает сообщения в системный журнал, и httpd, который обслуживает веб-страницы посредством работы протокола HTTP.
Свойства процессов-демонов:
- Имеют длинный жизненный цикл. Часто создаются во время загрузки системы и работают до момента ее отключения;
- Выполняются в фоновом режиме и не имеют котролируемого терминала. Это значит, что ядро не сможет генерировать для такого процесса никаких сигналов, связанных с терминалом или управлением заданиями(
SIGINT,SIGTSTP,SUGHUP);
Обычно демоны создаются для специфических задач:
cron— выполняет команды в запланированное время;sshd— демон защищенной командной оболочки, который позволяет входить в систему с удаленных компьютеров;httpd— демонHTTP-сервера для обслуживания веб-страниц;inetd— демонIP-службы, который ожидает входящих сетевых подключений на заданных портах и запускает соответствующие серверные программы для их обслуживания;
Создание процесса-демона
Для создания демона нужно выполнить следующие шаги:
- Сделать вызов
fork(), после которого процесс родителя завершается, а потомок продолжает работать. В итоге он становится потомком процессаinit. Делается это по следующим причинам:- Исходя из того, что демон был запущен в командной строке, завершение родителя будет обнаружено командной оболочкой, которая вслед за этим выведет новое приглашение и позволит потомку выполняться в фоновом режиме;
- Потомок гарантированно не станет лидером группы процессов, поскольку он наследует
PGIDот своего родителя и получает свой уникальный идентификатор, который отличается от унаследованногоPGID. Это необходимо для успешного выполнения следующего шага.
-
Дочерний процесс вызывает
setsid(), чтобы начать новую сессию и разорвать любые связи с контролирующим терминалом. - Если после этого демон больше не открывает никаких терминальных устройств, мы можем не волноваться о том, что он восстановит соединение с контролирующим терминалом. В противном случае нам необходимо сделать так, чтобы терминальное устройство не стало контролирующим. Это можно сделать двумя способами:
- Указывать флаг
O_NOCTTYдля любых вызововopen(), которые могут открыть терминальное устройство; - Есть более простой вариант: после
setsid()можно еще раз сделать вызовfork(), опять позволив родителю завершиться, а потомку (правнуку) — продолжить работу. Это гарантирует, что потомок не станет лидером сессии, что делает невозможным повторное соединение с контролирующим терминалом.
- Указывать флаг
-
Очистить атрибут
umaskпроцесса, чтобы файл и каталоги, созданные демоном, имели запрашиваемые права. -
Поменять текущий рабочий каталог процесса (обычно на корневой —
/). Это необходимо, поскольку демон обычно выполняется вплоть до выключения системы. Если файловая система, в которой находится его текущий рабочий каталог, не является корневой, то она не может быть отключена. Как вариант, в качестве рабочего каталога демон может задействовать то место, где он выполняет свою работу, или воспользоваться значением в конфигурационном файле. Главное, чтобы файловая система, в которой находится этот каталог, никогда не нуждалась в отключении. Например,cronприменяет для этого/var/spool/cron. -
Закрыть все открытые файловые дескрипторы, которые демон унаследовал от своего родителя (возможно, некоторые из них необходимо оставить открытыми, поэтому данный шаг является необязательным и может быть скорректирован). Это делается по целому ряду причин. Поскольку демон потерял свой контролирующий терминал и работает в фоновом режиме, ему больше не нужно хранить дескрипторы с номерами
0,1и2(если они ссылаются на терминал). Кроме того, мы не можем отключить файловую систему, в которой долгоживущий демон удерживает открытыми какие-либо файлы. И, следуя обычным правилам, мы должны закрывать неиспользуемые файловые дескрипторы, поскольку их число ограничено. - Закрыв дескрипторы с номерами
0,1и2, демон обычно перенаправляет их в предварительно открытый файл/dev/null, используя вызовdup2()(или похожий). Это делается по двум причинам:- Это позволяет избежать ошибки при вызове библиотечных функций, которые выполняют операции ввода/вывода с этими дескрипторами;
- Это исключает возможность повторного открытия демоном файлов с помощью дескрипторов
1или2, так как библиотечные функции, которые записывают в них данные, ожидают, что эти дескрипторы указывают на потокиstdout(стандартный вывод) иstderr(стандартный вывод ошибок).
Листинг процесса-демона
become_daemon.c:
#include <sys/stat.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/resource.h>
#include <fcntl.h>
#include <unistd.h>
#include "become_daemon.h"
int becomeDaemon(int flags) {
int maxfd, fd;
switch (fork()) {
case -1:
return -1;
case 0:
break;
default:
exit(EXIT_SUCCESS);
}
if (setsid() == -1) {
return -1;
}
switch (fork()) {
case -1:
return -1;
case 0:
break;
default:
exit(EXIT_SUCCESS);
}
if (!(flags & BD_NO_UMASK0))
umask(0);
if (!(flags & BD_NO_CHDIR))
chdir("/");
if (!(flags & BD_NO_CLOSE_FILES)) {
maxfd = sysconf(_SC_OPEN_MAX);
if (maxfd == -1)
maxfd = BD_MAX_CLOSE;
for (fd = 0; fd < maxfd; fd++)
close(fd);
}
if (!(flags & BD_NO_REOPEN_STD_FDS)) {
close(STDIN_FILENO);
fd = open("/dev/null", O_RDWR);
if (fd != STDIN_FILENO)
return -1;
if (dup2(STDIN_FILENO, STDOUT_FILENO) != STDOUT_FILENO)
return -1;
if (dup2(STDIN_FILENO, STDERR_FILENO) != STDERR_FILENO)
return -1;
}
return 0;
}
become_daemon.h:
#ifndef BECOME_DAEMON_H
#define BECOME_DAEMON_H
#define BD_NO_CHDIR 01 // Do not call chdir("/")
#define BD_NO_CLOSE_FILES 02 // Do not close all opend files
#define BD_NO_REOPEN_STD_FDS 04 // Do not forward stdin, stdout and stderr to /dev/null
#define BD_NO_UMASK0 010 // Do not call umask(0)
#define BD_MAX_CLOSE 8192 /* Maximum number of close file descriptors,
if sysconf(_SC_OPEN_MAX) does not defined */
int becomeDaemon(int flags);
#endif
Source
Linux API - Майкл Керриск. Глава 2. Основные понятия.
Linux API - Майкл Керриск. Глава 37. Демоны.