Ubuntu скрипт как демон

Обновлено: 05.07.2024

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

Первый способ

Запустить в сессии программы screen, которая не завершается при выходе пользователя.

screen -d -m команда

Пример:

screen -d -m -S backgroud_ping ping 127.0.0.1

Эта команда запустит пинг адреса 127.0.0.1 и присвоит сессии имя backgroud_ping.

Для возврата к приложению и получению управления нужно:

посмотреть список активный сессий screen:

в выводе консоли мы увидим имя и PID процесса:

There is a screen on:
1218.backgroud_ping (13.05.2016 15:43:34) (Detached)
1 Socket in /var/run/screen/S-root.

запущенная сессия будет иметь имя backgroud_ping, и в данном случае, PID будет 1218.

теперь остается подключиться к процессу screen, это делается командой:

screen -r номер_PID

в нашем случае это будет:

Мы получили обратно управление над приложением.

Второй способ

Использовать утилиту nohup, которая запустит приложение с игнорированием сигналов потери связи (SIGHUP), что позволит продолжить выполнение приложения после выхода пользователя из системы.

вывод будет перенаправлен вместо консоли в файл nohup.out, который будет находиться в папке из которой производился запуск. Посмотреть вывод консоли можно командой:

tail -f nohup.out

nohup ping 127.0.0.1 &

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

Третий способ

Использовать утилиту start-stop-daemon, которая создана для запуска в фоне приложений, которые сами не умеют переходить в фон.

start-stop-daemon -S -b -x путь_и_имя_исполняемого_файла -m -p путь_и_имя_pid_файла

start-stop-daemon -S -b -x /bin/ping -m -v -p /run/ping.pid -- -I eth0 127.0.0.1

В данном примере мы запускаем утилиту ping с параметрами (-I eth0 127.0.0.1) и создаем PID-файл (/run/ping.pid).

Для остановки программы использутся другие параметры:

start-stop-daemon -K -x путь_и_имя_исполняемого_файла

start-stop-daemon -K -p путь_и_имя_pid_файла

start-stop-daemon -K -p /run/ping.pid

Находим номер процесса, записанный в файл /run/ping.pid и останавливаем его.

Более правильно всегда использовать PID-файлы, потому что имя запускаемой программы не всегда равно имени запущенного процесса.

Заказать создание и поддержку безопасной IT-инфраструктуры любой сложности

Я пишу скрипт , который я хотел бы работать в качестве демона при запуске без использования внешних инструментов , таких как DaemonTools или демон .

Linux Daemon Writing HOWTO

Согласно Linux Daemon Writing HOWTO , правильный демон имеет следующие характеристики:

  • вилки из родительского процесса
  • закрывает все файловые дескрипторы (то есть stdin , stdout , stderr )
  • открывает логи для записи (если настроено)
  • изменяет рабочий каталог на постоянный (обычно / )
  • сбрасывает маску режима файла (umask)
  • создает уникальный идентификатор сеанса (SID)

Демонизация Введение

Демон Введение идет дальше, утверждая , что типичный демон также:

  • отсоединяется от терминала управления (если таковой имеется) и игнорирует все сигналы терминала
  • отделяется от своей группы процессов
  • ручки SIGCLD

Как бы сделать все это в sh , dash или bash сценарий только с общими инструментами Linux?

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

EDIT: демон (7) страница руководства также дает некоторые указатели, хотя , кажется, некоторые различия между старым стилем SysV демонами и новыми из systemd них. Поскольку совместимость с различными дистрибутивами важна, пожалуйста, убедитесь, что ответ проясняет любые различия.

«Правильный» способ создания собственного сценария оболочки состоит в том, чтобы заставить его вести свою собственную регистрацию, предоставить метод для запуска его в качестве демона и т. Д. Такие вещи daemon и другие вещи предназначены для запуска произвольных сценариев оболочки без предоставления возможности запуска как демон. Поскольку вы автор, полностью контролирующий, как пишется этот скрипт, сделайте так, чтобы его можно было просто запустить из файла systemd unitfile или rc.d. Вы же указать «Правильные»!

Используя systemd, вы сможете запустить скрипт как демон, создав простой модуль. Вы можете добавить множество различных опций , но это настолько просто, насколько вы можете получить.

Скажем, у вас есть сценарий /usr/bin/mydaemon .

Вы создаете юнит /etc/systemd/system/mydaemon.service .

Чтобы запустить демона, ты бежишь

Для запуска при загрузке вы включаете его

Если в системе, основанной на systemd, которой сегодня является большинство дистрибутивов Linux, это на самом деле не внешний инструмент. Негатив будет то, что он не будет работать везде, хотя.

Я написал Python script, который проверяет определенный адрес электронной почты и передает новые электронные письма во внешнюю программу. Как я могу заставить этот script выполнять 24/7, например, превратить его в демон или службу в Linux. Нужен ли мне цикл, который никогда не заканчивается в программе, или это можно сделать, просто повторив код повторно несколько раз?

ОТВЕТЫ

Ответ 1

У вас есть два варианта здесь.

Сделайте правильную работу cron, которая вызывает ваш скрипт. Cron - это общее имя для демона GNU/Linux, который периодически запускает сценарии в соответствии с заданным вами расписанием. Вы добавляете свой скрипт в crontab или помещаете символическую ссылку на него в специальный каталог, и демон выполняет его запуск в фоновом режиме. Вы можете прочитать больше в Википедии. Существует множество различных демонов cron, но в вашей системе GNU/Linux она должна быть уже установлена.

Используйте некоторый подход к Python (например, библиотеку), чтобы ваш скрипт мог демонизировать себя. Да, это потребует простой циклы событий (где ваши события запускаются по таймеру, возможно, обеспечивается функцией сна).

Я бы не рекомендовал вам выбирать 2., потому что вы на самом деле повторяете функциональность cron. Системная парадигма Linux - позволить нескольким простым инструментам взаимодействовать и решать ваши проблемы. Если нет дополнительных причин, по которым вы должны создавать демона (помимо периодического запуска), выберите другой подход.

Также, если вы используете daemonize с циклом и происходит сбой, никто не будет проверять почту после этого (как указал Иван Невоструев в комментариях к этому ответу). Хотя, если скрипт добавлен как задание cron, он просто сработает снова.

Ответ 2

Здесь хороший класс, который взят из здесь:

Ответ 3

Вы должны использовать библиотеку python-daemon, она заботится обо всем.

Из PyPI: Library для реализации хорошо зарекомендовавшего себя процесса демона Unix.

Ответ 4

Вы можете использовать fork(), чтобы отсоединить ваш script от tty и продолжить его, например:

Конечно, вам также нужно реализовать бесконечный цикл, например

Надеюсь, вы начнете.

Ответ 5

Вы также можете запустить python script как службу с помощью оболочки script. Сначала создайте оболочку script для запуска python script, как это (имя файла сценария)

теперь создайте файл в /etc/init.d/scriptname

Теперь вы можете запустить и остановить ваш python script с помощью команды /etc/init.d/scriptname start или stop.

Ответ 6

cron - отличный выбор для многих целей. Однако он не создает службу или демон, как вы просили в OP. cron просто запускает задания периодически (это означает, что задание запускается и останавливается) и не чаще, чем раз в минуту. Существуют проблемы с cron - например, если предыдущий экземпляр вашего script все еще работает при следующем расписании cron и запускает новый экземпляр, это нормально? cron не обрабатывает зависимости; он просто пытается начать работу, когда расписание говорит.

Если вы обнаружите ситуацию, когда вам действительно нужен демон (процесс, который никогда не перестает работать), взгляните на supervisord . Он предоставляет простой способ обернуть нормальную, не-daemonized script или программу и заставить ее работать как демон. Это намного лучший способ, чем создание собственного демона Python.

Ответ 7

Простая и поддерживаемая версия - это Deamonize Install it from Python Package Index (PyPI):

а затем использовать как:

Ответ 8

как использовать команду $nohup в linux?

Я использую его для запуска моих команд на моем сервере Bluehost.

Прошу совета, если я ошибаюсь.

Ответ 9

Сначала прочитайте псевдонимы почты. Почтовый псевдоним будет делать это в почтовой системе, если вам не придется обманывать демонов или сервисов или что-то в этом роде.

Если вы действительно хотите написать ненужный сложный сервер, вы можете сделать это.

Это все, что нужно. Ваш script просто петли и спит.

Ответ 10

Ответ 11

Если вы используете терминал (ssh или что-то еще), и вы хотите сохранить долговременную работу script после выхода из терминала, вы можете попробовать следующее:

apt-get install screen

создать виртуальный терминал внутри (а именно abc): screen -dmS abc

теперь мы подключаемся к abc: screen -r abc

Итак, теперь мы можем запустить python script: python Keep_sending_mail.py

с этого момента вы можете напрямую закрыть терминал, однако python script будет продолжать работать, а не закрываться

Так как этот Keep_sending_mail.py PID принадлежит виртуальному экрану, а не Терминал (SSH)

Если вы хотите вернуться, проверьте состояние работы script, вы можете снова использовать screen -r abc

Ответ 12

Используйте любой менеджер услуг, который предлагает ваша система, например, в Ubuntu используйте выскочку. Это будет обрабатывать все детали для вас, такие как запуск при загрузке, перезагрузка при сбое и т.д.

Ответ 13

Я бы рекомендовал это решение. Вам нужно наследовать и переопределить метод run .

Ответ 14

Он не использует ничего конкретного для любого дистрибутива, если используется chkconfig, при запуске системы.

Ответ 15

чтобы создать что-то, что работает как сервис, вы можете использовать эту вещь:

Первое, что вам нужно сделать, это установить Cement framework: Работа с рамкой Цемента - это работа кадра CLI, на которую вы можете развернуть свое приложение.

интерфейс командной строки приложения:

Имейте в виду, что ваше приложение должно запускаться в потоке, чтобы быть демоном

return -1;
>
else if (!pid) // если это потомок
// данный код уже выполняется в процессе потомка
// разрешаем выставлять все биты прав на создаваемые файлы,
// иначе у нас могут быть проблемы с правами доступа
umask(0);

// создаём новый сеанс, чтобы не зависеть от родителя
setsid();

// переходим в корень диска, если мы этого не сделаем, то могут быть проблемы.
// к примеру с размантированием дисков
chdir( "/" );

// закрываем дискрипторы ввода/вывода/ошибок, так как нам они больше не понадобятся
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);

// Данная функция будет осуществлять слежение за процессом
status = MonitorProc();

return status;
>
else // если это родитель
// завершим процес, т.к. основную свою задачу (запуск демона) мы выполнили
return 0;
>
>

* This source code was highlighted with Source Code Highlighter .

  • LoadConfig – данная функция загружает конфиг из указанного файла, её код будет зависеть от формата конфига, который вы используете, и в рамках данной статьи не будет рассматриваться.
  • Закрытие дескрипторов необходимо по той причине, что мы не будем использовать printf и scanf прочие функции работы с консольным вводом/выводом. Данное действие не обязательно и используется для экономии ресурсов.
  • Переход в корень диска, необходим для того, чтобы впоследствии не было проблем связанных с размонтированием дисков. Если текущая папка демона будет находиться на диске, который необходимо будет отмонтировать, то система не даст этого, до тех пор, пока демон не будет остановлен.
  • MonitorProc – данная функция будет выполнять основные действия, связанные с мониторингом состояния программы.
  1. Уведомление о завершении процесса демона.
  2. Получение кода завершения демона.

int MonitorProc()
int pid;
int status;
int need_start = 1;
sigset_t sigset;
siginfo_t siginfo;

// настраиваем сигналы которые будем обрабатывать
sigemptyset(&sigset);

// сигнал остановки процесса пользователем
sigaddset(&sigset, SIGQUIT);

// сигнал для остановки процесса пользователем с терминала
sigaddset(&sigset, SIGINT);

// сигнал запроса завершения процесса
sigaddset(&sigset, SIGTERM);

// сигнал посылаемый при изменении статуса дочернего процесса
sigaddset(&sigset, SIGCHLD);

// пользовательский сигнал который мы будем использовать для обновления конфига
sigaddset(&sigset, SIGUSR1);
sigprocmask(SIG_BLOCK, &sigset, NULL);

// данная функция создаст файл с нашим PID'ом
SetPidFile(PID_FILE);

// бесконечный цикл работы
for (;;)
// если необходимо создать потомка
if (need_start)
// создаём потомка
pid = fork();
>

// запустим функцию отвечающую за работу демона
status = WorkProc();

// завершим процесс
exit(status);
>
else // если мы родитель
// данный код выполняется в родителе

// ожидаем поступление сигнала
sigwaitinfo(&sigset, &siginfo);

// если пришел сигнал от потомка
if (siginfo.si_signo == SIGCHLD)
// получаем статус завершение
wait(&status);

// преобразуем статус в нормальный вид
status = WEXITSTATUS(status);

// прервем цикл
break ;
>
else if (status == CHILD_NEED_WORK) // если требуется перезапустить потомка
// запишем в лог данное событие
WriteLog( "[MONITOR] Child restart\n" );
>
>
else if (siginfo.si_signo == SIGUSR1) // если пришел сигнал что необходимо перезагрузить конфиг
kill(pid, SIGUSR1); // перешлем его потомку
need_start = 0; // установим флаг что нам не надо запускать потомка заново
>
else // если пришел какой-либо другой ожидаемый сигнал
// запишем в лог информацию о пришедшем сигнале
WriteLog( "[MONITOR] Signal %s\n" , strsignal(siginfo.si_signo));

// убьем потомка
kill(pid, SIGTERM);
status = 0;
break ;
>
>
>

// запишем в лог, что мы остановились
WriteLog( "[MONITOR] Stop\n" );

// удалим файл с PID'ом
unlink(PID_FILE);

* This source code was highlighted with Source Code Highlighter .

  • PID_FILE – константа, которая будет хранить имя файла для сохранения PID’a. В нашем случае это /var/run/my_daemon.pid
  • WriteLog – функция осуществляющая запись в лог. В ней вы можете придумать то, что душе угодно и писать лог куда угодно или вообще передавать его куда-нибудь
  • WorkProc – функция, которая реализует непосредственно функционал демона

f = fopen(Filename, "w+" );
if (f)
fprintf(f, "%u" , getpid());
fclose(f);
>
>

* This source code was highlighted with Source Code Highlighter .


На данный момент наш демон уже умеет запускаться, следить за своим потомком, который выполняет основные функции и при необходимости перезапускать его или посылать ему сигнал об изменение конфигурации. Далее рассмотрим шаблон кода потомка:

int WorkProc()
struct sigaction sigact;
sigset_t sigset;
int signo;
int status;

// сигналы об ошибках в программе будут обрататывать более тщательно
// указываем что хотим получать расширенную информацию об ошибках
sigact.sa_flags = SA_SIGINFO;
// задаем функцию обработчик сигналов
sigact.sa_sigaction = signal_error;

// установим наш обработчик на сигналы

sigaction(SIGFPE, &sigact, 0); // ошибка FPU
sigaction(SIGILL, &sigact, 0); // ошибочная инструкция
sigaction(SIGSEGV, &sigact, 0); // ошибка доступа к памяти
sigaction(SIGBUS, &sigact, 0); // ошибка шины, при обращении к физической памяти

// блокируем сигналы которые будем ожидать
// сигнал остановки процесса пользователем
sigaddset(&sigset, SIGQUIT);

// сигнал для остановки процесса пользователем с терминала
sigaddset(&sigset, SIGINT);

// сигнал запроса завершения процесса
sigaddset(&sigset, SIGTERM);

// пользовательский сигнал который мы будем использовать для обновления конфига
sigaddset(&sigset, SIGUSR1);
sigprocmask(SIG_BLOCK, &sigset, NULL);

// Установим максимальное кол-во дискрипторов которое можно открыть
SetFdLimit(FD_LIMIT);

// запишем в лог, что наш демон стартовал
WriteLog( "[DAEMON] Started\n" );

// остановим все рабочеи потоки и корректно закроем всё что надо
DestroyWorkThread();
>
else
WriteLog( "[DAEMON] Create work thread failed\n" );
>

WriteLog( "[DAEMON] Stopped\n" );

// вернем код не требующим перезапуска
return CHILD_NEED_TERMINATE;
>

* This source code was highlighted with Source Code Highlighter .

  • InitWorkThread — функция которая создаёт все рабочие потоки демона и инициализирует всю работу.
  • DestroyWorkThread — функция которая останавливает рабочие потоки демона и корректно освобождает ресурсы.
  • ReloadConfig — функция осуществляющая обновление конфига (заново считать файл и внести необходимые изменения в свою работу). Имя файла можно также взять из параметров командной строки.

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

Обработка ошибок при работе, с подробным отчетом в лог.

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

Код функции обработчика ошибок:

static void signal_error( int sig, siginfo_t *si, void *ptr)
void * ErrorAddr;
void * Trace[16];
int x;
int TraceSize;
char ** Messages;

// запишем в лог что за сигнал пришел
WriteLog( "[DAEMON] Signal: %s, Addr: 0x%0.16X\n" , strsignal(sig), si->si_addr);

// произведем backtrace чтобы получить весь стек вызовов
TraceSize = backtrace(Trace, 16);
Trace[1] = ErrorAddr;

// получим расшифровку трасировки
Messages = backtrace_symbols(Trace, TraceSize);
if (Messages)
WriteLog( "== Backtrace ==\n" );

// запишем в лог
for (x = 1; x < TraceSize; x++)
WriteLog( "%s\n" , Messages[x]);
>

WriteLog( "== End Backtrace ==\n" );
free(Messages);
>

WriteLog( "[DAEMON] Stopped\n" );

// остановим все рабочие потоки и корректно закроем всё что надо
DestroyWorkThread();

// завершим процесс с кодом требующим перезапуска
exit(CHILD_NEED_WORK);
>

* This source code was highlighted with Source Code Highlighter .

При использовании backtrace можно получить данные примерно такого вида:
[DAEMON] Signal: Segmentation fault, Addr: 0x0000000000000000
== Backtrace ==
/usr/sbin/my_daemon(GetParamStr+0x34) [0x8049e44]
/usr/sbin/my_daemon(GetParamInt+0x3a) [0x8049efa]
/usr/sbin/my_daemon(main+0x140) [0x804b170]
/lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe6) [0x126bd6]
/usr/sbin/my_daemon() [0x8049ba1]
== End Backtrace ==

Из этих данных видно, что функция main вызвала функцию GetParamInt. Функция GetParamInt вызвала GetParamStr. В функции GetParamStr по смещению 0x34 произошло обращение к памяти по нулевому адресу.

Также помимо стека вызовов можно сохранить и значение регистров (массив uc_mcontext.gregs).
Необходимо заметить, что наибольшую информативность от backtrace можно получить только при компилировании без вырезания отладочной информации, а также с использованием опции -rdynamic.

Как можно было заметить, в коде используются константы CHILD_NEED_WORK и CHILD_NEED_TERMINATE. Значение этих констант вы можете назначать сами, главное чтобы они были не одинаковые.

Некоторые вопросы связанные с ресурсами системы.

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

int SetFdLimit( int MaxFd)
struct rlimit lim;
int status;

// зададим текущий лимит на кол-во открытых дискриптеров
lim.rlim_cur = MaxFd;
// зададим максимальный лимит на кол-во открытых дискриптеров
lim.rlim_max = MaxFd;

// установим указанное кол-во
status = setrlimit(RLIMIT_NOFILE, &lim);

* This source code was highlighted with Source Code Highlighter .

Вместо заключения.
Вот мы и рассмотрели как создать основу для демона. Конечно же код не претендует на идеальный, но со своими задачами справляется отлично.
В следующей статье будут рассмотрены моменты, связанные с установкой/удалением демона, управления им, написанием скриптов автозагрузки для init.d и непосредственно добавлением в автозагрузку.

В операционных системах семейства Linux запущенные фоном скрипты и программы, которые не взаимодействуют напрямую с пользователем, называют демонами. Зачастую, но не всегда, демоны загружаются вместе с операционной системой. Самым ярким примером демона может служить сервер сетевого протокола или же менеджеры для периодического выполнения задач ( cron ).

Демон процессы можно классически создать используя системные вызовы fork и execve с отключенной оболочкой для потока или нового процесса, однако для нас сегодня это слишком низкий уровень. Более простым инструментом для создания и контроля состояния демонов является systemd.

Systemd - относительно новая подсистема иницализации и управления демонами Linux. Первый выпуск данной подсистемы был осуществлён в марте 2010 года, сама подсистема была призвана заменить существующую систему init. Данная подсистема оперирует таким понятием, как модуль. А аналогом демонов является понятие "служба", это один из подвидов модуля, далее в статье демоны будут пониматься как службы.

Рассмотрим поближе модули

  • .target - модуль для группировки модулей, позволяет реализовывать концепцию уровней запуска
  • .service - отвечает за запуск сервисов (служб )или, как мы их называем, демонов;
  • .mount - отвечает за монтирование файловых систем;
  • .automount - по сути то же самое, что и mount, с той лишь разницей, что вызывается при фактическом обращении к точке монтирования;
  • .swap - модули для работы с файлом подкачки;
  • .timer - модули позволяющие организовать запуск других модулей по расписанию;
  • .socket — предоставляет службам поддержку механизма сокет-активации;
  • .slice — отвечает за создание контейнера cgroups;
  • .device — позволяет реагировать на подключение устройств;
  • .path — управляет иерархией файловой системы.

Непосредственно создание демона

Теперь перейдём непосредственно к созданию демона. Я буду показывать пример создания демона на основе скрипта LogCleaner, который позволяет чистить директории от файлов, сохраняя структуру директорий. Работу разделим на 3 части:

  1. Скрипт установки нашего демона;
  2. Модуль типа .service для запуска нашего скрипта как демона;
  3. Модуль типа .timer для периодического запуска демона;

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

Далее, чтобы при удалении пользователей в системе наш скрипт не удалился вместе с домашней директорией, выберем место хранения. Идеальным местом будет директория /opt , к ней имеют доступ все пользователи, а если скрипт вдруг нужно удалить, то достаточно будет убрать его оттуда ( главное не забыть удалить модули ).
После того, как мы скопировали скрипт в директорию opt, нужно скопировать наши модули. Все системные модули systemd хранит в директории /etc/systemd/system , туда их и скопируем.
Далее останется активировать таймер и добавить его в автозагрузку.
Таким образом получаем скрипт установки для оболочки bash:

2. Модуль типа .service
Основным файлом, который и будет запускать наш скрипт, является файл с расширением .service. Формат модулей systemd напоминает файлы формата .ini, в модулях systemd также есть секции и директивы, пример:
Для создания простого демона нам нужны всего 2 секции: Unit и Service. В секцию Unit мы добавим описание нашего сервиса, а в секции Service укажем какой файл нужно запускать и как. В итоге получим такой файл:

3. Модуль типа .timer
Последнее, что нам остаётся написать - это модуль типа .timer. Данный модуль будет запускать наш основной сервис раз в пять минут. Для данного модуля нам нужны секции Unit, Timer и Install.
В секции Unit также будет описание нашего сервиса.
В секции Timer мы добавим, что хотим запустить сервис через минуту после старта системы и после каждые 5 минут.
В секции Install мы укажем какой сервис мы хотим запускать.

Читайте также: