Гид по технологиям

Как создать демон в Linux

8 min read System Programming Обновлено 05 Jan 2026
Как создать демон в Linux
Как создать демон в Linux

Демон, запущенный в терминале Linux

Введение: что такое демон

Демоны — это процессы, которые не управляются напрямую активным пользователем и работают в фоне. Обычно они запускаются при старте системы и работают непрерывно до выключения. Главное практическое отличие от обычных процессов — отсутствие обмена с управляющим терминалом (не выводят интерактивно сообщения на экран).

Примеры распространённых демонов в Linux:

  • crond — выполняет задачи по расписанию (cron jobs)
  • sshd — обеспечивает удалённый вход в систему по SSH
  • httpd/nginx — обслуживает веб-запросы
  • nfsd — обеспечивает сетевой доступ к файлам

Часто имя демона оканчивается на букву «d» (например, sshd), но это лишь конвенция, а не требование.

Что происходит при демонизации: краткое описание процесса

Перед тем как стать демоном, процесс обычно выполняет начальные операции: чтение конфигурации, попытки получить ресурсы и валидацию. Это нужно, чтобы при ошибках система могла корректно сообщить о них и завершить процесс с кодом ошибки.

Стандартный путь демонизации включает следующие шаги:

  1. Выполнение первичной инициализации (чтение конфигурации, проверка прав). Если что-то не так — сообщить и завершиться.
  2. fork: создать дочерний процесс и завершить родительский — это даёт дочернему процессу init (systemd) в качестве родителя.
  3. setsid: создать новую сессию и отделиться от управляющего терминала.
  4. (Опционально) второй fork, чтобы не быть лидером сессии и предотвратить получение управляющего терминала в будущем.
  5. Сброс umask (обычно в 0) и переход в корневую директорию или другую безопасную рабочую директорию.
  6. Закрытие всех наследованных файловых дескрипторов.
  7. Перенаправление stdin, stdout, stderr в /dev/null или в лог-файлы.
  8. (Рекомендуется) запись PID-файла и установка обработчиков сигналов (SIGHUP, SIGTERM).

Ниже подробно рассмотрим сессии и группу процессов, а затем перейдём к коду и практическим рекомендациям.

Что такое сессии и группы процессов

После входа в систему через терминал пользователь запускает приложения через оболочку. Операционная система группирует процессы в группы процессов (process groups) и сессии, чтобы корректно управлять вводом/выводом и сигналами.

Каждая сессия состоит из одной или нескольких групп процессов. Контролирующий терминал (controlling terminal) связан только с одной сессией в каждый момент времени.

Диаграмма сессий демон-процессов

Идентификаторы сессии и группы процессов — это PID лидеров соответствующих групп. Дочерний процесс наследует группу от родителя. Когда несколько процессов связаны через пайп, первый процесс становится лидером группы.

Пример: простая демонизация (пошагово с кодом)

В исходной статье приведён пример из двух файлов: test.c и daemon.c. Ниже сохранены и прокомментированы эти фрагменты с объяснениями.

// test.c
#include 

int _daemon(int, int);

int main()
{
    getchar();
    _daemon(0, 0);
    getchar();
    return 0;
}

Файл демонстрирует точку вызова функции демонизации _daemon. Первый getchar() позволяет увидеть состояние процесса до демонизации.

// daemon.c
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

int _daemon(int nochdir, int noclose) {
    pid_t pid;
    pid = fork(); // Fork off the parent process
    if (pid < 0) {
        exit(EXIT_FAILURE);
    }
    if (pid > 0) {
        exit(EXIT_SUCCESS);
    }
    return 0;
}

В этом простейшем варианте дочерний процесс остаётся в фоне, поскольку родитель завершился. Однако до вызова setsid и закрытия дескрипторов демон не полностью отделён от управляющего терминала.

Скомпилируйте и запустите так:

gcc -o test test.c daemon.c
./test

Используйте ps для просмотра состояния процесса до и после вызова _daemon.

Пример вывода до демонизации (когда _daemon ещё не вызван):

ps -C test -o "pid ppid pgid sid tty stat command"

# Output
PID    PPID    PGID     SID TT       STAT COMMAND
10296  5119   10296    5117 pts/2    S+   ./test

Поле STAT показывает состояние процесса. Обозначения:

АббревиатураЗначение
SОжидание события (спящий)
TПроцесс остановлен
sЛидер сессии
+Процесс находится на переднем плане

Родителем вашего приложения на этом этапе является оболочка:

ps -jp 5119
# Output
PID    PGID     SID TTY          TIME CMD
5119   5119    5117 pts/2    00:00:02 zsh

После вызова _daemon и завершения родительского процесса:

ps -C test -o "pid ppid pgid sid tty stat command"

# Output
PID    PPID    PGID     SID TT       STAT COMMAND
22504     1   22481    5117 pts/2    S    ./test

Родителем процесса стал PID 1 (systemd):

ps -jp 1
# Output
PID    PGID     SID TTY          TIME CMD
1      1        1   ?        00:00:01 systemd

Чтобы окончательно отделиться от терминала и создать новую сессию, нужно вызвать setsid:

if (setsid() == -1)
    return -1;

После вызова setsid вы увидите, что процесс больше не связан с tty (поле TT будет «?»), а он становится лидером сессии.

Далее обычно делают chdir(“/“) и закрывают дескрипторы. Пример добавления chdir:

if (!nochdir) {
    if (chdir("/") == -1)
        return -1;
}

Закрытие файловых дескрипторов и перенаправление stdin/stdout/stderr:

#define NR_OPEN 1024
if (!noclose) {
    for (i = 0; i < NR_OPEN; i++)
        close(i);
    open("/dev/null", O_RDWR);
    dup(0);
    dup(0);
}

После этого дескрипторы 0, 1 и 2 будут указывать на /dev/null, и любые printf попадут в соответствующий поток, а не в терминал.

Полный пример простой функции _daemon:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

int _daemon(void) {
    // PID: Process ID
    // SID: Session ID
    pid_t pid, sid;
    pid = fork(); // Fork off the parent process
    if (pid < 0) {
        exit(EXIT_FAILURE);
    }
    if (pid > 0) {
        exit(EXIT_SUCCESS);
    }
    // Create a SID for child
    sid = setsid();
    if (sid < 0) {
        // FAIL
        exit(EXIT_FAILURE);
    }
    if ((chdir("/")) < 0) {
        // FAIL
        exit(EXIT_FAILURE);
    }
    close(STDIN_FILENO);
    close(STDOUT_FILENO);
    close(STDERR_FILENO);
    while (1) {
        // Some Tasks
        sleep(30);
    }
    exit(EXIT_SUCCESS);
}

Пример использования системного вызова daemon(3) (в libc) — упрощённый способ демонизации, но не всегда предпочтителен:

if (!(debug_flag || inetd_flag || no_daemon_flag)) {
    int fd;
    if (daemon(0, 0) < 0)
        fatal("daemon() failed: %.200s", strerror(errno));
    /* Disconnect from the controlling tty. */
    fd = open(_PATH_TTY, O_RDWR | O_NOCTTY);
    if (fd >= 0) {
        (void) ioctl(fd, TIOCNOTTY, NULL);
        close(fd);
    }
}

Важно: многие современные дистрибутивы и best practices рекомендуют НЕ демонизировать процессы из кода, а запускать их в foreground и управлять ими через systemd или другой инициализатор. Если программа демонизируется внутри себя, systemd не сможет корректно отслеживать её состояние и управлять рестартами (если только не настроена Type=forking и правильно настроен PIDFile).

Улучшенные практики: что добавить к базовой демонизации

Ниже — набор рекомендаций и шаблонов, которые обычно используют в реальных демонах.

  • Umask: сбросьте umask, например umask(0), чтобы файлы/директории создавались с предсказуемыми правами (контролируйте сами в момент создания).
  • Второй fork: выполните второй fork после setsid, чтобы процесс не оставался лидером сессии. Это предотвращает повторное получение управляющего терминала.
  • PID-файл: создавайте /var/run/your-daemon.pid (или /run/…) и записывайте в него PID процесса. Проверяйте существующий PID и жив ли процесс, чтобы предотвратить двойной запуск.
  • Логи: используйте syslog или выход в файл. Для systemd лучше логировать в stdout/stderr и позволять systemd-journald собирать логи.
  • Обработчики сигналов: установите обработчики для SIGTERM (корректное завершение), SIGHUP (перечитать конфиг) и SIGCHLD (если есть дочерние процессы).
  • Проверка привилегий: если демону не нужны root-привилегии — сделайте drop privileges (setuid/setgid) после открытия необходимых ресурсов.
  • Безопасность: ограничьте доступ к PID и к директориям конфигурации, используйте chroot по необходимости и capabilities, если требуется.

Примерная последовательность действий (мини‑методология):

  1. Выполнить начальную проверку конфигурации и ресурсов в foreground.
  2. Заблокировать файл устройства/порт при необходимости (например, bind на порт).
  3. Fork -> родитель выходит, дочерний вызывает setsid.
  4. Второй fork (опционально).
  5. chdir(“/“) и umask(0).
  6. Закрыть дескрипторы, перенаправить 0/1/2 на /dev/null или на лог.
  7. Записать PID-файл и установить корректные права.
  8. Установить обработчики сигналов и начать главный цикл.

systemd против классической демонизации

Современные системные менеджеры (systemd) предпочитают, чтобы сервисы работали в foreground и не демонизировались самостоятельно. Для systemd существуют типы юнитов:

  • Type=simple — сервис запускается и остаётся в foreground (рекомендуется для большинства приложений).
  • Type=forking — процесс форкается в фон (используется для старых демонов; systemd ожидает, что процесс запишет PID-файл).

Если вы планируете поддержку systemd, лучше:

  • Не демонизируйте процесс внутри кода; добавьте флаг –no-daemon чтобы выключить демонизацию.
  • Пусть systemd контролирует рестарт и логирование.

Пример простого unit-файла systemd (пример):

[Unit]
Description=My Example Daemon
After=network.target

[Service]
Type=simple
User=myuser
ExecStart=/usr/bin/my-daemon --no-daemon
Restart=on-failure

[Install]
WantedBy=multi-user.target

Критерии приёмки (общие тесты)

  • Процесс корректно запускается и переходит в фон (или остаётся в foreground, если требуется).
  • PID-файл создаётся и содержит действительный PID, при остановке PID-файл удаляется.
  • При SIGTERM процесс завершает работу в пределах ожидаемого времени.
  • При SIGHUP конфигурация перечитывается без утечек ресурсов.
  • Логирование осуществляется в указанном месте; нет вывода в управляющий tty.
  • При попытке запустить второй экземпляр — корректный отказ или использование механизма блокировки.

Контроль качества, тестовые кейсы

  • Функциональный тест: запуск/останов/перезапуск сервиса.
  • Нагрузочный тест: сервис устойчив при пиковых запросах и не порождает утечек памяти.
  • Тест устойчивости: симулировать потерю сети, внешних зависимостей и убедиться, что сервис ведёт себя предсказуемо.
  • Безопасность: проверить права на PID-файлы, доступ к логам, запуск не от root.

Когда демонизация не нужна — контрпример

  • Есть systemd (или другой современный менеджер) — предпочтительнее запуск в foreground.
  • Приложение должно быть контейнеризовано (Docker) — там ожидают foreground-процесс.
  • Для короткоживущих задач демонизация усложняет отладку и мониторинг.

Риски и рекомендации по безопасности

  • Демонизация под root без drop-privileges увеличивает риск компрометации. Открывайте привилегированные ресурсы как root, затем переключайтесь на непривилегированный пользователь.
  • Ведение логов в файлы может раскрывать секреты; ограничьте доступ и используйте журналирование с ротацией.
  • Корректно обрабатывайте сигналы, чтобы избегать неконсистентных состояний при завершении.

Чек-листы по ролям

Разработчик:

  • Добавить флаг –no-daemon для запуска в foreground
  • Реализовать запись PID-файла и проверку уже работающего процесса
  • Поддержать корректные обработчики SIGHUP/SIGTERM
  • Предусмотреть логирование в stdout/stderr или syslog

Системный администратор:

  • Писать systemd unit для управления сервисом
  • Контролировать права на PID/лог-файлы
  • Настроить ротацию логов (logrotate или journald)

Оператор DevOps:

  • Настроить мониторинг и alerting (SLO/SLI по доступности и времени отклика)
  • Запланировать обновления и безболезнственные перезапуски

Шаблон: упрощённый decision flow (Mermaid)

flowchart TD
  A[Запуск сервиса] --> B{Нужна ли демонизация в коде?}
  B -- Да --> C[Реализовать демонизацию: fork, setsid, chdir, close fds]
  B -- Нет --> D[Запускать в foreground; настроить systemd unit]
  C --> E{Записывать PID-файл?}
  E -- Да --> F[Создать /run/your.pid и контролировать блокировку]
  E -- Нет --> G[systemd Type=forking или нет PID]
  D --> H[Type=simple, Restart политики, логирование в stdout]

Короткая сводка: что важно помнить

  • Демонизация — это не только fork, но и отделение от сессии, закрытие дескрипторов и корректное управление правами и логами.
  • Для новых сервисов предпочтительнее работать в foreground и доверить систему управления (systemd).
  • Всегда обрабатывайте сигналы и реализуйте безопасное завершение и перезапуск.

Краткий глоссарий (1 строка на термин)

  • Демон: фоновой процесс, не привязанный к управляющему терминалу.
  • PID-файл: файл с PID запущенного процесса, обычно в /run или /var/run.
  • setsid: системный вызов для создания новой сессии и отделения от tty.
  • umask: маска создания прав для новых файлов/директорий.

Быстрые рекомендации по отладке

  • Запускайте с –no-daemon и используйте gdb/strace для отладки.
  • Логи переправляйте в stdout при работе с systemd, чтобы использовать journalctl.
  • Проверяйте все возвращаемые коды системных вызовов и логируйте ошибки при старте.

Заключение

Понимание демонизации важно для системного программирования и администрирования Linux. Хотя классический алгоритм демонизации остаётся полезным для изучения устройства системы и старых демонов, современные практики рекомендуют делегировать управление процессами systemd и писать сервисы, которые корректно работают в foreground и оставляют оркестрацию системе.

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

RDP: полный гид по настройке и безопасности
Инфраструктура

RDP: полный гид по настройке и безопасности

Android как клавиатура и трекпад для Windows
Гайды

Android как клавиатура и трекпад для Windows

Советы и приёмы для работы с PDF
Документы

Советы и приёмы для работы с PDF

Calibration в Lightroom Classic: как и когда использовать
Фото

Calibration в Lightroom Classic: как и когда использовать

Отключить Siri Suggestions на iPhone
iOS

Отключить Siri Suggestions на iPhone

Рисование таблиц в Microsoft Word — руководство
Office

Рисование таблиц в Microsoft Word — руководство