Демонизация с обратной связью

Author / Автор: Сергей Сацкий
Publication date / Опубликовано: 28.11.2005
Version / Версия текста: 1.0

Введение

Типичная схема работы большинства нетривиальных приложений представлена ниже:


Рисунок 1. Блок - схема работы приложения

На этапе инициализации, как правило, производится захват требуемых приложению ресурсов, например, выделение памяти. На этапе завершения обычно выполняют освобождение захваченных ресурсов. Захват требуемых ресурсов может окончиться ошибкой, например по причине недостатка требуемых аппаратных ресурсов. В этом случае приложение обычно выдает сообщение об ошибке и возвращает операционной системе код возврата, соответствующий возникшей ситуации. Сообщение об ошибке обычно попадает на стандартный поток вывода ошибок (stderr) или в файл протокола. Иногда вместо stderr сообщение помещают в стандартный поток вывода (stdout).

В случае с демоном (программой, работающей в фоновом режиме и не имеющей стандартного ввода и вывода) ситуация несколько осложняется. Демонизация, то есть устранение зависимости от управляющего терминала и вызывающей оболчки, чаще всего строится с помощью системного вызова fork(). Процесс - родитель играет роль стартовой площадки для процесса - потомка, который и выполняет фоновую работу. Выполнять захват ресурсов в процессе - родителе не имеет смысла, так как эти ресурсы используются только в процессе - потомке. Значит, ошибка стадии инициализации может возникнуть только в процессе - потомке. Однако операционная система получит код возврата, который можно проанализировать, только от процесса - родителя. В плохом сценарии ситуация может выглядеть следующим образом:

  • Операционная система запускает демон.
  • Процесс - родитель создает потомка.
  • Процесс - потомок начинает выполнять инициализацию.
  • Процесс - родитель завершает свою работу. Операционная система получает код успешного завершения. Пользователь, возможно, извещается об успешном старте демона.
  • Процесс - потомок не может выполнить захват требуемых ресурсов и завершается с соответствующим кодом возврата. Код возврата никто не анализирует.
  • В приведенном сценарии пользователь дезинформирован о реальном старте приложения. Конечно, покопавшись в файле протокола, можно будет найти соответствующее сообщения, но как об этом догадаться? Еще хуже, если контроль старта осуществляется программами на основе кодов возврата.
  • Для исправления ситуации необходимо приостановить процесс - родитель до тех пор, пока процесс - потомок не завершит этап инициализации и не сигнализирует об успехе / неуспехе процессу - родителю. После получения сигнализации процесс - родитель завершит работу с соответствующим кодом возврата.

Скелет

Скелет демонизации с обратной связью использует два сигнала: SIGUSR1 для сигнализации родителю об успешном окончании инициализации и SIGUSR2 для сигнализации о неуспешном окончании инициализации. Все необходимые функции и переменные содержатся в одном заголовочном файле SDaemonFramework.h. Для обеспечения демонизации потребуется только включить заголовочный файл, вызвать функцию Daemonise() и после завершения инициализации вызвать соответствующую функцию SignalOK() или SignalFailed() для обеспечения обратной связи.


. . .

#include "SDaemonFramework.h"

int   main( int  argc, char **  argv )
{
    Daemonise();

        // Здесь уже демон - начинаем инициализацию

    . . .

    if ( . . . )
    {
            // Может быть оставляем сообщение в файле протокола и т.п.
        SignalFailed();
        return 1;
    }

        // Все хорошо, продолжаем работать
    SignalOK();

    . . .

    return 0;
}

Поддержка отладки и безопасность

Отладка демона сложнее, чем отладка обычного приложения. Для облегчения отладки иногда удобно иметь возможность скомпилировать приложение как демон или как обычное приложение. Скелет демонизации с обратной связью поддерживает такую возможность с помощью директив условной компиляции. Переменная DAEMON управляет работой скелета. В Makefile можно написать, например, вот так:

. . .
# Два возможных значения: 0 - обычное приложение, 1 - демон
DAEMON=1
. . .
main.o: main.cpp
    g++ -c -DDAEMON=$\{DAEMON} main.cpp
. . .

Таким образом, для смены типа приложения потребуется изменить значение переменной в одном месте. По умолчанию приложение будет компилироваться как обычное, без демонизации.

Для обеспечения безопасности запуска демона из любого рабочего каталога без потенциальной блокировки файловой системы необходимо сменить рабочий каталог демона. Скелет поддерживает возможность задания каталога через переменную условной компиляции HOME. Если переменная не определена, то по умолчанию будет производиться смена рабочего каталога на корневой.

Замечания

В заголовочном файле SDaemonFramework.h содержатся тела необходимых функций и переменные. Все содержимое заголовочного файла помещено в анонимное пространство имен. Это позволяет избежать ошибок этапа связывания, даже если заголовочный файл включен в несколько единиц компиляции. Однако следует помнить, что каждое не нужное включение приведет к накладным расходам - итоговый исполняемый файл будет чуть-чуть больше.

СОВЕТ

Включайте SDaemonFramework.h только один раз в головной модуль приложения.

Управляющий скрипт

Привычный скрипт запуска демона в стиле SystemV поддерживает команды start, stop, restart и status. В процессе старта операционной системы подобные скрипты запускают необходимые демоны и печатают [OK] или [FAILED] на консоле. Скрипт обычно опирается на код возврата запускаемого демона. С учетом реализованной обратной связи скрипт будет корректно информировать о старте демона. В качестве шаблона скрипта можно воспользоваться любым из уже готовых.

Окружение

Код проверен и отлажен на операционной системе Linux Red Hat 7.3 и компиляторе gcc 3.4.3. Скорее всего, разработанный код будет работать также хорошо и на других комбинациях ОС - компилятор.

Исходный текст простейшего примера можно найти на сайте автора http://satsky.spb.ru.

Список литературы

1. Андрей Робачевский. Операционная система UNIX. СПб., БХВ-Петербург, 2000


Verbatim copying and distribution of this entire article is permitted in any medium, provided this notice is preserved.

Разрешается копирование и распространение этой статьи любым способом без внесения изменений, при условии, что это разрешение сохраняется.
Last Updated: December 6, 2005