Как написать демона linux

Обновлено: 02.07.2024

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

У меня уже почти готов код изменения файловой системы, но я не могу понять, как создать демона.

Что делать после вилка?

в Linux я хочу добавить демона, который не может быть остановлен и который отслеживает изменения файловой системы. Если какие-либо изменения будут обнаружены, он должен написать путь к консоли, где он был запущен + новая строка.

кроме того, есть несколько необходимые шаги демонизировать процесс.

если я правильно помню, эти шаги являются:

  • вилки off родительский процесс и пусть он завершится, если разветвление было успешным. - >Поскольку родительский процесс завершен, дочерний процесс теперь выполняется в фоновом режиме.
  • setsid - создать новый сеанс. Вызывающий процесс становится руководитель новой сессии и руководитель группы процессов новой группы процессов. Процесс теперь отделен от своего контролируя терминала (CTTY).
  • сигналы ловить - игнорировать и/или обработки сигналов.
  • вилка снова & пусть родительский процесс завершится, чтобы убедиться, что вы избавитесь от ведущего процесса сеанса. (Только лидеры сессии могут снова получить TTY.)
  • перейти - изменение рабочего каталога демон.
  • атрибуты - измените маску режима файла в соответствии с потребностями демона.
  • закрыть - закрыть все открытые файловые дескрипторы, которые могут быть унаследованы от родительского процесса.

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

  • скомпилировать код: gcc -o firstdaemon daemonize.c
  • старт демон: ./firstdaemon

проверьте, все ли работает правильно: ps -xj | grep firstdaemon

вывод должен быть похож на этот:

что вы должны увидеть вот это:

  • у демона нет управляющего терминала (TTY = ?)
  • идентификатор родительского процесса (PPID) is 1 (процесс инициализации)
  • в PID != Сид это означает, что наш процесс не является лидером сессии
    (из-за второй вилки())
  • потому что PID != SID наш процесс не могу снова взять под контроль TTY

чтение лога:

Do a: grep firstdaemon /var/log/syslog

вывод должен быть похож на этот один:

Примечание: На самом деле вы также хотите реализовать обработчик сигнала и правильно настроить ведение журнала (файлы, уровни журнала. ).

читайте далее:

man 7 daemon описывает, как создать демона в деталях. Мой ответ - просто выдержка из этого руководства.

существует по крайней мере два типа демонов:

Демоны SysV

если вы заинтересованы в традиционных SysV демон, вы должны использовать тег следующее:

  1. закройте все открытые файловые дескрипторы, кроме standard вход, выход и (т. е. первые три файловых дескриптора 0, 1, 2). Это гарантирует, что случайно переданный файловый дескриптор не останется в процессе демона. В Linux это лучше всего реализовать путем итерации через /proc/self/fd , с запасного прохода из файлового дескриптора 3 в значение, возвращенное getrlimit() на RLIMIT_NOFILE .
  2. сброс все обработчики сигналов по умолчанию. Это лучше всего сделать путем перебора доступных сигналов до предела _NSIG и сброс их в SIG_DFL .
  3. сброс маски сигнала с помощью sigprocmask() .
  4. очистите блок среды, удалив или сбросив переменные среды, которые могут негативно повлиять демон во время выполнения.
  5. вызов fork() , чтобы создать фоновый процесс.
  6. в детстве, вызов setsid() для отсоединения от любого терминала и создания независимого сессии.
  7. в детстве, вызов fork() опять же, чтобы гарантировать, что демон никогда не сможет снова получить терминал.
  8. вызов exit() в первом ребенке, так что только второй ребенок (the фактический процесс демона) остается вокруг. Это гарантирует, что процесс демона повторно передается init/PID 1, как и все демоны.
  9. в процессе демона подключите /dev/null стандартные вход, выход и .
  10. В процесса, сброс umask до 0, так что режимы файлов передаются в open() , mkdir() и т. п. непосредственно управлять режимом доступа к созданным файлам и каталогам.
  11. процесс демона, изменить текущий каталог в корневой каталог ( / ), чтобы избежать того, что демон непроизвольно блокирует точки монтирования от демонтажа.
  12. в процессе демона напишите демон PID (как возвращено getpid() ) в PID-файл, например /run/foobar.pid (для гипотетического демона "foobar") для убедитесь, что демон не может быть запущен более одного раза. Это должно быть реализовано без гонки, так что PID-файл обновляется только тогда, когда он проверяется в то же время, что PID, ранее сохраненный в PID-файле, больше не существует или принадлежит внешнему процессу.
  13. в процессе демона удалите привилегии, если это возможно и применимо.
  14. из процесса демона уведомите начатый исходный процесс о завершении инициализации. Это может быть реализовано через безымянный канал или аналогичный канал связи, который создается до первого fork() и, следовательно, доступны как в исходном, так и в демоническом процессе.
  15. вызов exit() в исходном процессе. Процесс, который вызвал демона, должен иметь возможность полагаться на то, что это exit() происходит после инициализация завершена и все внешние каналы связи установлены и доступный.

обратите внимание на это предупреждение:

BSD daemon() функции не должен использоваться, так как он реализует только подмножество из этих шагов.

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

обратите внимание, что daemon() не POSIX совместимость.

Демоны Нового Стиля

для демонов нового стиля следующее рекомендуется:

  1. если SIGTERM получено, выключите демон и выйдите чисто.
  2. если SIGHUP is получено, перезагрузите файлы конфигурации, если это применимо.
  3. укажите правильный код выхода из основного процесса демона, так как он используется системой init для обнаружения ошибок и проблем службы. Рекомендуется следовать схеме кода выхода, определенной в рекомендации LSB для сценариев инициализации SysV.
  4. если это возможно и применимо, откройте интерфейс управления демона через D-Bus IPC система и захватить имя автобуса, как последний шаг инициализации.
  5. для интеграции в systemd, предоставить .сервисблок файл, содержащий информацию о запуске, остановке и ином обслуживании демона. См. systemd.service(5) для сведения.
  6. насколько это возможно, полагайтесь на функциональность системы init, чтобы ограничить доступ демона к файлам, сервисам и другим ресурсам, т. е. в случае systemd, полагайтесь на systemd ресурс ограничение управления вместо реализации собственного, полагайтесь на systemd's привилегии сбрасывая код вместо его реализации в Демоне и тому подобное. См. systemd.exec(5) для существующих элементов управления.
  7. если D-Bus используется, сделайте вашу шину демона активируемой, поставив активацию службы D-Bus конфигурационный файл. Это имеет несколько преимуществ: ваш демон может быть запущен лениво по требованию; он может быть запущен в параллельно с другими демонами, требующими этого - что максимизирует распараллеливание и загрузки скорость; ваш демон может быть перезапущен при сбое без потери запросов шины, так как шины очереди запросов для активируемых служб. См.ниже для сведения.
  8. если ваш демон предоставляет услуги другим локальным процессам или удаленным клиентам через сокет, это должно быть сделано socket-activatable следуя схеме, указанной ниже. Как и активация D-Bus, это позволяет запускать службы по требованию, а также позволяет улучшить распараллеливание запуска службы. Кроме того, для протоколов без состояний (таких как syslog, DNS) демон, реализующий активацию на основе сокетов, можно перезапустить без потери одного запроса. См.ниже для сведения.
  9. если применимо, демон должен уведомить систему init о завершении запуска или обновлениях состояния через sd_notify(3) интерфейс.
  10. вместо syslog() вызов для входа непосредственно в службу системного журнала, демон нового стиля может просто войти в стандартную ошибку через fprintf() , который затем перенаправляется в системный журнал системой init. Если уровни журнала необходимы, они могут быть закодированы путем префикса отдельных строк журнала со строками типа " "(для уровня журнала 4 "предупреждение" в схеме приоритета системного журнала), следуя аналогичному стиль как ядро Linux printk() система уровней. Подробнее см. В разделе sd-daemon(3) и systemd.exec(5) .

чтобы узнать больше, читайте все man 7 daemon .

Я могу остановиться на первом требовании " демон который нельзя остановить . "

не возможно, мой друг; однако, вы можете достичь того же с гораздо лучшим инструментом, модуль ядра.

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

вам может понадобиться более общая функция демонизации, где вы можете указать имя вашей программы / демона и путь для запуска вашей программы (возможно, " / " или "/tmp"). Вы также можете предоставить файлы для stderr и stdout (и, возможно, путь управления с использованием stdin).

вот необходимые включает в себя:

и вот более общая функция,

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

обратите внимание, что SIG_IGN указывает поймать и игнорировать сигнал. Вы можете создать обработчик сигнала, который может регистрировать прием сигнала, и установить флаги (например, флаг для указания изящного завершения работы).

попробуйте использовать daemon функция:

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

Если nochdir равен нулю, daemon() изменяет текущий вызывающий процесс рабочий каталог в корневой каталог ( " / " ); в противном случае рабочий каталог остается без изменений.

Если noclose равно нулю, daemon() перенаправляет стандартный ввод, стандартный вывод и стандартная ошибка в /dev / null; в противном случае никаких изменений этих дескрипторов файлов.

если ваше приложение является одним из:

и вы не против зависимости NodeJS, затем установите NodeJS, а затем:

чтобы все приложения работали при перезагрузке (и daemonise pm2):

(также легко позволяет следить за изменениями кода в каталоге приложений и автоматически перезапускать процесс приложения, когда происходит изменение кода)

вызывая fork (), вы создали дочерний процесс. Если fork успешен (fork вернул ненулевой PID), выполнение будет продолжаться с этой точки из дочернего процесса. В этом случае мы хотим изящно выйти из родительского процесса, а затем продолжить работу в дочернем процессе.

демон-это просто процесс в фоновом режиме. Если вы хотите запустить программу при загрузке ОС в linux, добавьте команду "Пуск" в /etc/rc.г/RC.локальный (запуск после всех других скриптов) или /etc/startup.sh

в windows вы создаете службу, регистрируете службу, а затем устанавливаете ее для автоматического запуска при загрузке в панели Администрирование -> Службы.

Перевод Linux Daemon HOWTO

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

Версия 1.0, май 2004
------------------------------------------------------------ ------------
В этом документе рассказывается о том, как писать демонов в Linux с использованием GCC. Для полного понимания этого документа необходимы знание Linux и языка программирования C. Права на этот документ в терминах лицензии BSD принадлежат Девину Ватсону.
------------------------------------------------------------ ------------

1. Введение: что есть демон?

3. Планирование вашего демона
* 3.1 Что необходимо сделать?
* 3.2 Насколько интерактивные?

4. Базовая структура демона
* 4.1 Ответвление от родительского процесса
* 4.2 Изменение маски файла (Umask)
* 4.3 Открытие журналов на запись
* 4.4 Создание уникального ID сессии (SID)
* 4.5 Изменение рабочего каталога
* 4.6 Закрытие стандартных файловых дескрипторов

5. Реализация кода демона
* 5.1 Инициализация
* 5.2 Большой Цикл

6. Собираем все вместе
* 6.1 Законченный пример


1. Введение: что есть демон?

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

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

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

* GCC 3.2.2 или выше
* библиотеки и заголовочные файлы для разработки в Linux

Если в вашей системе они еще не установлены (что вряд ли, но все равно проверьте), то они понадобятся вам для реализации примеров из этого документа. Чтобы узнать версию установленного в вашей системе GCC, скомандуйте:

3. Планирование вашего демона

3.1 Что необходимо сделать?
Демон должен делать только одну вещь и делать ее хорошо. Эта одна вещь может быть такой же сложной как управление сотнями почтовых ящиков во множестве доменов или такой же простой как формирование отчета и вызов sendmail для оправки этого отчета админу.

В любом случае вы должны хорошо представлять себе то, что должен делать ваш демон. Если планируется взаимодействие с другими демонами, разрабатываемыми и вами, и не вами, то это тоже необходимо учитывать при планировании.

3.2 Насколько интерактивные?
Демоны не должны общаться с пользователем напрямую через терминал. На самом деле, демон вообще не должен напрямую общаться с пользователем. Все общение должно производиться через определенный интерфейс (который может позволять, а может и не позволять запись), который может быть сложным как GTK+ GUI или простым как набор сигналов.


4. Базовая структура демона

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

* Отделение (ответвление, fork) от родительского процесса
* Изменение файловой маски (umask)
* Открытие любых журналов на запись
* Создание уникального ID сессии (SID)
* Изменение текущего рабочего каталога на безопасное место
* Закрытие стандартных файловых дескрипторов
* Переход к коду собственно демона

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


Отметим здесь проверку успешного завершения вызова fork(). При разработке демона необходимо делать код максимально стабильным. На самом деле, большую часть всего кода демона составляют именно проверки на ошибки.

Функция fork() возвращает либо id дочернего процесса (PID, не равный нулю), либо -1 в случае ошибки. Если процесс не может породить потомка, то демон должен завершиться прямо здесь.

Если получение PID от fork() совершилось успешно, то родительский процесс должен изящно завершиться. Это может показаться странным для всех, кто такого еще не видел, но после ответвления дочерний процесс продолжает выполнение остального кода с этого места.

4.2 Изменение файловой маски (Umask)
Чтобы иметь возможномть писать в любые файлы (включая журналы), созданные демоном, файловая маска (umask) должна быть изменена так, чтобы они могли быть записаны или прочитаны правильным образом. Это похоже на выполнение umask из командной строки, но мы прагматично делаем это здесь при помощи функции umask():


Через установку umask в 0 мы получим полный доступ к файлам, созданным демоном. Даже если вы не планируете использовать какие-либо файлы вообще, установка umask остается хорошей идеей просто на случай доступа к файлам на файловой системе.

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

4.4 Создание уникального ID сессии (SID)
С этого места для нормальной работы дочерний процесс должен получить уникальный SID от ядра. Иначе дочерний процесс станет сиротой. Тип pid_t, объявленный в предыдущем разделе, также используется для создания нового SID для дочернего процесса:


Как видим, функция setsid() возвращает данные того же типа что и fork(). Чтобы проверить что функция создала SID для дочернего процесса мы можем использовать аналогичную процедуру проверки на ошибки.

4.5 Изменение рабочего каталога
Текущий рабочий каталог нужно сменить на некоторое место, гарантированно присутствующее в системе. Поскольку многие дистрибутивы Linux не полностью следуют стандарту иерархии файловой системы Linux (FHS, Filesystem Hierarchy Standard), то в системе гарантированно присутствует только корень файловой системы (/). Сменить каталог можно при помощи функции chdir():


И снова мы видим здесь код обработки ошибок. Функция chdir() при ошибке возвращает -1, так что не забывайте проверять возвращаемое ею значение после смены каталога.

4.6 Закрытие стандартных файловых дескрипторов
Одним из последних шагов в стартовой настройке демона является закрытие стандартных файловых дескрипторов (STDIN, STDOUT, STDERR). Поскольку демон не может использовать терминал, эти файловые дескрипторы излишни и создают угрозу безопасности. Закрыть их можно при помощи функции close():


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


5. Разработка кода демона

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

Важный момент здесь в том, что при инициализации чего-нибудь в демоне необходимо применять те же методы обнаружения и обработки ошибок. Будьте болтливы ("verbose") насколько это возможно при записи в syslog или в ваши собственные журналы. Отладка демона может затрудниться при недостатке информации о его состоянии.

5.2 Большой Цикл
Основной код демона обычно находится внутри бесконечного цикла. Технически это не бесконечный цикл конечно, но он организован как бесконечный:


Типичный цикл обычно является циклом while, который имеет бесконечное условие завершения с вызовом sleep при необходимости выполнения через фиксированные интервалы.

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

6. Собираем все вместе

6.1 Законченный пример
Приведенное ниже является законченным примером демона и иллюстрирует все шаги, необходимые для запуска и работы. Для его выполнения просто скимпилируйте при помощи gcc и запустите на выполнение из командной строки. Для завершения выясните его PID и воспользуйтесь командой kill.

Я также задействовал подходящие заголовочные файлы для взаимодействия с syslog'ом, использование которого рекомендуется как минимум для записи в журнал информации о запуске/останове/паузе/завершении, в дополнение к использованию ваших собственных журналов через вызовы функций fopen()/fwrite()/fclose().


Вы можете использовать этот скелет для разработки ваших собственных демонов. Не забывайте вести журналы событий (или используйте возможности syslog) и кодируйте надежно, кодируйте надежно, кодируйте надежно!

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 и непосредственно добавлением в автозагрузку.

(1) Режим работы : Демон, также известный как Daemon Процессы в Linux Фоновый сервис Процесс. Периодически выполняйте определенные задачи или ожидайте обработки определенных событий. Системы Linux имеют много демонов, большинство из них обслуживание Все реализованы с использованием демонов.

Например: как наши tftp, samba, nfs и другие связанные сервисы. В то же время большинство серверов в Linux реализованы с использованием демонов.

(2 ) Жизненный цикл: Демоны работают в течение длительного времени, часто запускаются при запуске системы и не завершаются, пока система не выключится

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

Посмотрите на демонов в системе

A -a: показать все

-X: показать процессы без управления терминалом

-J: Показать информацию о задании (отображаемые столбцы): идентификатор сеанса (SID), идентификатор группы процессов (PGID), терминал управления (TTY), идентификатор группы процессов терминала (TRGID)

Как идентифицировать демона:

  1. Обычно все демоны запускаются как суперпользователи (UID равен 0);
  2. Нет терминала управления (TTY?);
  3. Идентификатор группы процессов терминала равен -1 (TPGID представляет идентификатор группы процессов терминала, это значение представляет группу процессов переднего плана, связанную с управляющим терминалом, и, если она не связана с каким-либо терминалом, его значение равно -1;
  4. Родительским процессом всех демонов является процесс init (процесс с PID 1).

Например, процесс планирования работы cron


В-четвертых, концепции, относящиеся к сеансу: группа процессов, группа процессов переднего плана, группа фоновых процессов, сеанс, терминал управления

1) Группа процессов

Группа процессов - это совокупность одного или нескольких процессов. Группа процессов однозначно идентифицируется по идентификатору группы процессов (PGID). Каждая группа процессов имеет процесс лидера, а идентификатор группы процессов - это номер процесса лидера.

2) Сессия

Коллекция - это одна или несколько групп процессов. Как правило, пользователь создает новый сеанс (открывает терминал) после входа в систему, и каждый сеанс также имеет идентификатор для его идентификации (SID). Первый процесс после входа в систему называетсяВедущий сессии session leader ), Обычно shell / bash. Для процесса лидера сеанса его PID = SID.

3) Терминал управления

Сессия обычно имеет управляющий терминал для выполнения IO Операция. Терминал, в который входит пользователь, становится управляющим терминалом для сеанса. Процесс лидера сеанса, который устанавливает соединение с управляющим терминалом, также называетсяКонтроль процесса (controlling process) 。 Для сеанса может быть только один управляющий терминал.

4) группа процессов переднего плана

Процессы в этой группе процессов могут выполняться на терминальном устройстве. Читать, писать Группа процессов операции.

5) группа фоновых процессов

Процессы в этой группе процессов могут сообщать только терминальному устройству. Написать 。

Каждый сеанс имеет одну и только одну группу процессов переднего плана, но будет 0 Одна или несколько групп фоновых процессов.

6) Идентификатор группы процессов терминала

Каждый процесс также имеет атрибут, идентификатор группы процессов терминала (TPGID), который используется для определения того, входит ли процесс в группу процессов, связанную с терминалом. Процессы в группе приоритетных процессов имеют TPGID = PGID, а группа фоновых процессов имеет PGID ≠ TPGID. Если процесс не связан ни с одним терминалом, его значение равно -1. Сравните их, чтобы определить, принадлежит ли процесс группе переднего плана или группе фоновых процессов.


Например

$ ping 127.0.0.1 | grep icmp > temp & Передайте две инструкции вместе и поместите на задний план

$ ping 127.0.0.1 | grep icmp Запустить на переднем плане

Посмотреть результаты


Мы узнали

  1. SID одинаковы, что указывает на то, что все участвуют в сеансе. И значение SID равно 13792. Bash является лидером сессии
  2. Существует три группы процессов 13792, 14433 и 14435. Мы можем видеть, что ping и grep, связанные через |, находятся в группе процессов.
  3. Группа процессов 14435 - это группа процессов переднего плана, потому что TPGID = PGID, 14433, 13792 - группа фоновых процессов.

После выполнения Ctrl + C в терминале


  1. Все процессы в группе процессов 14435 исчезли, что указывает на то, что сигнал был отправлен всей группе процессов переднего плана.
  2. 13792 Процессы сгруппированы в новую группу процессов переднего плана TPGID = PGID

Первый процесс, созданный при открытии нового терминала, - это оболочка, которая является ведущим процессом сеанса. По умолчанию ведущий процесс находится в группе процессов переднего плана, а управляющий терминал открывается для чтения и записи данных. При запуске команды в оболочке (без &) для создания новой группы процессов, если в командной строке несколько команд, будет создано несколько процессов. Эти процессы находятся во вновь созданной группе процессов. Оболочка создаст новую группу процессов. Установите группу в качестве группы процессов переднего плана и временно установите себя в качестве группы фоновых процессов.

Пять шагов написания Linux-демона

Step1: Создать дочерний процесс, родительский процесс завершен

По сути Сделать ini t Процесс становится родителем вновь порожденного процесса. вызов fork После того, как функция создает дочерний процесс, она немедленно завершает работу родительского процесса. В результате дочерний процесс станет сиротским процессом и будет init Процесс вступает во владение, и в то же время новый сгенерированный процесс будет работать в фоновом режиме, используя описанный ранее родительский процесс, ядро ​​автоматически поручит дочернему процессу init Принцип.

if (pid > 0) /* Родительский процесс завершается */

Step2: Выйдите из терминала управления и создайте новый сеанс

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

Возвращаемое значение: в случае успеха вернуть новый идентификатор сеанса, а в случае ошибки - -1.

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

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

Шаг 3: изменить текущий рабочий каталог

Этот шаг также необходим. Дочерний процесс, созданный с помощью fork (), наследует текущий рабочий каталог родительского процесса. Поскольку файловая система (такая как «/ mnt / usb» и т. Д.), В которой находится текущий каталог, не может быть размонтирована во время выполнения процесса, это вызовет много проблем для будущего использования. Поэтому обычной практикой является использование «/» в качестве текущего рабочего каталога демона, что позволяет избежать вышеуказанных проблем. Конечно, если у вас есть особые потребности, вы также можете изменить текущий рабочий каталог на другой путь, например / tmp. Распространенной функцией для изменения рабочего каталога является chdir ().

Step4: Сбросить маску прав доступа к файлу

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

Step5: Отключить файловые дескрипторы

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

int getdtablesize(void);

Возвращает: максимальное количество файловых дескрипторов, открытых процессом (1024)

Запретить процессам повторное открытие терминала управления

Теперь процесс стал главой бессессионного терминала. Но он может повторно открыть контрольный терминал. Вы можете помешать процессу повторно открыть управляющий терминал, сделав процесс больше не лидером сеанса:

if(pid=fork())
exit (0); // Завершаем первый дочерний процесс, и второй дочерний процесс продолжается (второй дочерний процесс больше не является лидером сеанса)

Интеллектуальная рекомендация


Краткое описание общих функций MPI

содержание 1, основная функция MPI 2, точка-точка функция связи 3, коллективная функция связи 1, основная функция MPI MPI_Init(&argc, &argv) Информировать системы MPI для выполнения всех необх.

Примечание 9: EL выражение


JVM память

концепция Виртуальная машина JVM управляет собственной памятью, которая разделяет память во многие блоки, наиболее распространенной для памяти стека и памяти кучи. 1 структура виртуальной машины JVM H.

Проблема сетевого запроса на Android 9.0

вЗапустите Android 9 (API Уровень 28) или вышеНа устройстве операционной системы Android, чтобы обеспечить безопасность пользовательских данных и устройств, использование по умолчанию для зашифрованно.


Учебная запись по Webpack (3) В статье рассказывается о создании webpack4.0.

предисловие Для изучения веб-пакета автор также предпринял много обходных путей. Есть много вещей, которые я хочу знать, но я не могу их найти. Автор поможет вам быстро начать работу. Цель этой статьи.

В Linux я хочу добавить демон, который нельзя остановить и который отслеживает изменения файловой системы. Если обнаружены какие-либо изменения, он должен записать путь к консоли, с которой он был запущен, плюс новую строку.

У меня уже есть почти готовый код изменения файловой системы, но я не могу понять, как создать демона.

Что делать после форка?

Если вам не нужно соответствие POSIX, вас может заинтересовать inotify API. См: inotify_init , inotify_add_watch , inotify_rm_watch .

В Linux я хочу добавить демон, который нельзя остановить и который отслеживает изменения файловой системы. Если будут обнаружены какие-либо изменения, он должен записать путь к консоли, с которой она была запущена, + новую строку.

Кроме того, есть несколько необходимых шагов, чтобы демонизировать процесс.

Если я правильно помню, это следующие шаги:

  • отвалить от родительского процесса и пусть это прекратить , если разветвление было успешным. -> Поскольку родительский процесс завершился, дочерний процесс теперь работает в фоновом режиме.
  • setsid - Создать новую сессию. Вызывающий процесс становится лидером нового сеанса и лидером группы процессов новой группы процессов. Теперь процесс отключен от своего управляющего терминала (CTTY).
  • Поймать сигналы - игнорировать и / или обрабатывать сигналы.
  • fork снова и позвольте родительскому процессу завершиться, чтобы гарантировать, что вы избавитесь от ведущего процесса сеанса. (Только руководители сеанса могут снова получить TTY.)
  • chdir - изменить рабочий каталог демона.
  • umask - Измените маску режима файла в соответствии с потребностями демона.
  • close - закрыть все дескрипторы открытых файлов, которые могут быть унаследованы от родительского процесса.

Чтобы дать вам отправную точку: посмотрите на этот скелетный код, который показывает основные шаги. Этот код теперь также можно форкнуть на GitHub: Базовый скелет демона Linux

  • Скомпилируйте код: gcc -o firstdaemon daemonize.c
  • Запустите демон: ./firstdaemon

Проверьте, все ли работает правильно: ps -xj | grep firstdaemon

Результат должен быть похож на этот:

Здесь вы должны увидеть:

  • У демона нет управляющего терминала ( TTY =? )
  • Идентификатор родительского процесса ( PPID ) равен 1 (процесс инициализации).
  • PID! = SID , который означает , что наш процесс не является лидером сеанса
    (из второй вилки ())
  • Поскольку PID! = SID, наш процесс не может снова взять под контроль TTY

Чтение системного журнала:

    Найдите свой файл системного журнала. Моя здесь: /var/log/syslog

Сделайте: grep firstdaemon /var/log/syslog

Результат должен быть похож на этот:


Примечание: на самом деле вы также можете реализовать обработчик сигналов и правильно настроить ведение журнала (файлы, уровни журналов . ).

Дальнейшее чтение:

Вау, спасибо! Замечательно. Итак, я должен поместить свой код в цикл while, и все? В основном да. Но этот код - всего лишь пример. Это полностью зависит от того, чего вы хотите достичь с помощью процесса-демона. Обязательно прочтите и этот ответ: @Edwin Вместо второго fork() , почему бы просто не использовать setsid() ? Обратите внимание, что эта sigaction() функция обеспечивает более полный и надежный механизм управления сигналами; новые приложения должны использовать, sigaction() а не signal() .

man 7 daemon подробно описывает, как создать демона. Мой ответ - всего лишь выдержка из этого руководства.

Есть как минимум два типа демонов:

SysV Демоны

Если вас интересует традиционный демон SysV , вы должны выполнить следующие шаги :

  1. Закройте все дескрипторы открытых файлов, кроме стандартного ввода , вывода и ошибок (т.е. первых трех файловых дескрипторов 0, 1, 2). Это гарантирует, что в процессе демона не останется случайно переданный дескриптор файла. В Linux это лучше всего реализовать путем итерации /proc/self/fd с резервным вариантом итерации от файлового дескриптора 3 к значению, возвращаемому getrlimit() for RLIMIT_NOFILE . все обработчики сигналов к их значениям по умолчанию. Лучше всего это сделать, перебирая доступные сигналы до предела _NSIG и сбрасывая их на SIG_DFL .
  2. Сбросьте маску сигнала с помощью sigprocmask() .
  3. Очистите блок среды, удалив или сбросив переменные среды, которые могут негативно повлиять на время выполнения демона.
  4. Звоните fork() , чтобы создать фоновый процесс.
  5. В дочернем вызове setsid() отключение от любого терминала и создание независимого сеанса .
  6. В дочернем элементе вызовите еще fork() раз, чтобы демон никогда не смог повторно получить терминал.
  7. Вызовите exit() первого дочернего элемента, чтобы остался только второй дочерний процесс (фактический процесс демона). Это гарантирует, что процесс-демон будет повторно связан с init / PID 1, как и должны быть все демоны.
  8. В процессе демона подключитесь /dev/null к стандартному вводу , выводу и ошибке .
  9. В процессе демона, сбросить umask в 0, так что режимы файл передается open() , mkdir() и таковою непосредственно контролировать режим доступа к файлам и каталогам.
  10. В процессе демона измените текущий каталог на корневой каталог ( / ), чтобы демон случайно не блокировал размонтирование точек монтирования.
  11. В процессе демона запишите PID демона (возвращенный getpid() ) в файл PID, например /run/foobar.pid (для гипотетического демона «foobar»), чтобы гарантировать, что демон не может быть запущен более одного раза. Это должно быть реализовано без гонок, чтобы файл PID обновлялся только тогда, когда он проверяется, в то же время, когда PID, ранее сохраненный в файле PID, больше не существует или принадлежит стороннему процессу.
  12. В процессе демона удалите привилегии, если это возможно и возможно.
  13. Из процесса демона уведомить запущенный исходный процесс о завершении инициализации. Это может быть реализовано через безымянный канал или аналогичный канал связи, который создается до первого fork() и, следовательно, доступен как в исходном процессе, так и в процессе демона.
  14. Звоните exit() в исходный процесс. Процесс, вызвавший демона, должен полагаться на то, что это exit() произойдет после завершения инициализации и установления и доступности всех внешних каналов связи.

Обратите внимание на это предупреждение:

BSD daemon() функция не должна быть использована, поскольку она реализует только подмножество этих шагов.

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

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