Daemons (Русский)
Состояние перевода: На этой странице представлен перевод статьи Daemons. Дата последней синхронизации: 23 декабря 2021. Вы можете помочь синхронизировать перевод, если в английской версии произошли изменения.
Демон — это программа, которая запускается в фоновом режиме (без терминала или пользовательского интерфейса), ожидая событий и предлагая какие-то службы для их выполнения. Хорошим примером демона может служить веб-сервер, ожидающий запроса на доставку страницы, или сервер SSH, ожидающий чьего-нибудь логина. Существуют приложения, действия которых видны, а работа демонов не видна. К ним относятся, например, демон, записывающий системные сообщения в журналы (syslog, metalog), или демон, держащий время системы точным (Ntpd). Для получения дополнительной информации смотрите страницу справочного руководства daemon(7) .
В Arch Linux демонами управляет systemd. Команда systemctl используется для управления ими в пользовательском интерфейсе. systemd читает файлы .service, которые содержат информацию о том, как и когда запустить связанный демон. Файлы служб хранятся в каталогах //systemd/system . Для получения дополнительной информации смотрите раздел systemd (Русский)#Использование юнитов.
Retrieved from «https://wiki.archlinux.org/index.php?title=Daemons_(Русский)&oldid=766386»
Что такое демон?
Как заядлый пользователь компьютера, вы, возможно, часто слышали термин «демон». Но что он означает на самом деле?
Этот странный термин — пережиток истории Unix, но демоны используются и сегодня. Этот термин является синонимом понятия «сервис» — задачи, которая выполняется постоянно, обычно без участия пользователя.
Существует несколько распространенных примеров демонов, и даже если вам не нужно знать, как именно они работают, по крайней мере, полезно уметь их идентифицировать.
Что такое демон?
Daemon или Disk And Execution MONitor — это программа, которая работает в фоновом режиме, а не в интерактивном. Демоны широко распространены среди сетевых инструментов и задач системного администрирования. Вы можете управлять некоторыми демонами, посылая им сигналы.
Как произносится слово «демон»?
Несмотря на кажущуюся очевидность, слово «Daemon» обычно произносится так же, как и слово «демон». Будьте уверены, оно не имеет ничего общего с сатанинскими ритуалами. На самом деле, название происходит из греческой мифологии и концепции духа-проводника, неустанно работающего на заднем плане. В этом смысле это очень подходящее название!
Как работают демоны?
В системах Unix термин демон часто используется как взаимозаменяемый с термином фоновый процесс. Другими словами, это процесс, который был отсоединен от любой оболочки и продолжает выполняться неинтерактивно. Настоящий демон также должен назначить себя дочерним процессом специального процесса init, который имеет специальный идентификатор процесса с номером 1.
Другие типичные задачи, связанные с изоляцией демона, включают:
- Создание «песочницы» путем удаления ненужных переменных из его окружения.
- Установка корневого каталога (/) в качестве текущего рабочего каталога процесса.
- Перенаправление описаний файлов ввода, вывода и ошибок в файлы журналов или /dev/null.
В других системах, таких как Windows и macOS, также используются демоны. В случае Windows ближайшим эквивалентом являются службы.
Каковы некоторые распространенные демоны?
Cron (crond) — это демон, который выполняет другие программы по расписанию. Большинство Unix-систем настраивают его на автоматический запуск при запуске. Он просыпается раз в минуту, проверяет конфигурационные файлы и выполняет все команды, заданные для запуска в течение этой минуты. Cron достаточно умен, чтобы реагировать на изменения в файлах crontab, когда они меняются. Вам не нужно перезапускать его самостоятельно.
Веб-серверы, такие, как httpd Apache, часто работают как демоны — именно это означает буква «d». Демон Apache обрабатывает входящие запросы, создавая пул дочерних процессов для распределения нагрузки. Вы можете использовать вспомогательную программу, такую как apachectl, для более легкого контроля над самим демоном httpd.
Наконец, sshd — еще один распространенный пример демона, на этот раз для обработки SSH-соединений. Опять же, он обычно запускается при загрузке и работает неограниченное время, полагаясь на сигналы для выполнения определенных операций. Например, сигнал SIGHUP заставляет sshd повторно выполнить себя, перезагружая при этом свой конфигурационный файл.
Демоны являются поставщиками жизненно важных услуг
Демоны работают постоянно, выполняя жизненно важные задачи для поддержания работоспособности компьютера. Они часто задействуются, когда компьютеру необходимо работать в качестве сервера, действуя от имени других клиентов.
Демоны являются жизненно важным компонентом сервера Linux. Если вы новичок в настройке сервера, ознакомьтесь с нашими советами по началу работы.
Что такое демоны в Linux
Демоны много работают, для того, чтобы вы могли сосредоточится на своем деле. Представьте, что вы пишите статью или книгу. Вы заинтересованны в том, чтобы писать. Удобно, что вам не нужно вручную запускать принтер и сетевые службы, а потом следить за ними весь день для того чтобы убедится, что всё работает нормально.
За это можно благодарить демонов, они делают эту работу за нас. В сегодняшней статье мы рассмотрим что такое демоны в Linux, а также зачем они нужны.
Что такое демоны в понятии Linux
Демон Linux — это программа, у которой есть определённая уникальная цель. Обычно, это служебные программы, которые незаметно работают в фоновом режиме для того чтобы отслеживать состояние и обслуживать определённые подсистемы и гарантировать правильную работу всей операционной системы в целом. Например, демон принтера, отслеживает состояние служб печати, а сетевой демон управляет сетевыми подключениями и следит за их состоянием.
Многие люди, перешедшие в Linux из Windows знают демонов как службы или сервисы. В MacOS термин «Служба» имеет другое значение. Так как MacOS это тоже Unix, в ней испольуются демоны. А службами называются программы, которые находятся в меню Службы.
Демоны выполняют определённые действия в запланированное время или в зависимости от определённых событий. В системе Linux работает множество демонов, и каждый из них предназначен для того чтобы следить за своей небольшой частью операционной системы. Поскольку они не находятся под непосредственным контролем пользователя, они фактически невидимы, но тем не менее необходимы. Поскольку демоны выполняют большую часть своей работы в фоновом режиме, они могут казаться загадочными.
Какие демоны работают на вашем компьютере
Обычно имена процессов демонов заканчиваются на букву d. В Linux принято называть демоны именно так. Есть много способов увидеть работающих демонов. Они попадаются в списке процессов, выводимом утилитами ps, top или htop. Но больше всего для поиска демонов подходит утилита pstree. Эта утилита показывает все процессы, запущенные в вашей системе в виде дерева. Откройте терминал и выполните такую команду:
Вы увидите полный список всех запущенных процессов. Вы можете не знать за что отвечают эти процессы, но они все будут здесь перечислены. Вывод pstree — отличная иллюстрация того, что происходит с вашей машиной. Здесь удобно найти запущенные демоны Linux.
Вот демоны Linux, которых вы можете здесь увидеть: udisksd, gvfsd, systemd, logind и много других. Список процессов довольно длинный, поэтому он не поместится в одном окне терминала, но вы можете его листать.
Запуск демонов в Linux
Давайте разберемся как запустить демона Linux. Ещё раз. Демон — это процесс, работающий в фоновом режиме и находится вне контроля пользователя. Это значит, что демон не связан с терминалом, с помощью которого можно было бы им управлять. Процесс — это запущенная программа. В каждый момент времени он может быть запущенным, спящим или зомби (процесс выполнивший свою задачу, но ожидающий пока родительский процесс примет результат).
В Linux существует три типа процессов: интерактивные, пакетные и демоны. Интерактивные процессы пользователь запускает из командной строки. Пакетные процессы обычно тоже не связанны с терминалом. Они запускаются обычно во время когда на систему минимальная нагрузка и делают свою работу. Это могут быть, например, скрипты резервного копирования или другие подобные обслуживающие сценарии.
Интерактивные и пакетные процессы нельзя считать демонами, хотя их можно запускать в фоновом режиме и они делают определённую работу. Ключевое отличие в том, что оба вида процессов требуют участия человека. Демонам не нужен человек для того чтобы их запускать.
Когда загрузка системы завершается, система инициализации, например, systemd, начинает создавать демонов. Этот процесс называется forking (разветвление). Программа запускается как обычный интерактивный процесс с привязкой к терминалу, но в определённый момент она делится на два идентичных потока. Первый процесс, привязанный к терминалу может выполнятся дальше или завершится, а второй, уже ни к чему не привязанный продолжает работать в фоновом режиме.
Существуют и другие способы ветвления программ в Linux, но традиционно для создания дочерних процессов создается копия текущего. Термин forking появился не из ниоткуда. Его название походит от функции языка программирования Си. Стандартная библиотека Си содержит методы для управления службами, и один из них называется fork. Используется он для создания новых процессов. После создания процесса, процесс, на основе которого был создан демон считается для него родительским процессом.
Когда система инициализации запускает демонов, она просто разделяется на две части. В таком случае система инициализации будет считаться родительским процессом. Однако в Linux есть ещё один метод запуска демонов. Когда процесс создает дочерний процесс демона, а затем завершается. Тогда демон остается без родителя и его родителем становится система инициализации. Важно не путать такие процессы с зомби. Зомби, это процессы, завершившие свою работу и ожидающие пока родительский процесс примет их код выхода.
Примеры демонов в Linux
Самый простой способ определить демона — это буква d в конце его названия. Вот небольшой список демонов, которые работают в вашей системе. Каждый демон создан для выполнения определённой задачи.
- systemd — основная задача этого демона унифицировать конфигурацию и поведение других демонов в разных дистрибутивах Linux.
- udisksd — обрабатывает такие операции как: монтирование, размонтирование, форматирование, подключение и отключение устройств хранения данных, таких как жесткие диски, USB флешки и т д.
- logind — небольшой демон, управляющий авторизацией пользователей.
- httpd — демон веб-сервера, позволяет размещать на компьютере или сервере веб-сайты.
- sshd — позволяет подключаться к серверу или компьютеру удалённо, по протоколу SSH.
- ftpd — организует доступ к компьютеру по протоколу FTP для передачи файлов.
- crond — демон планировщика, позволяющий выполнять нужные задачи в определённое время.
Как появился термин демон в Linux
Так откуда же взялся этот термин? На первый взгляд может показаться, что у создателей операционной системы просто было искаженное чувство юмора. Но это не совсем так. Это слово появилось в вычислительной технике ещё до появления Unix. А история самого слова ещё более древняя.
Изначально это слово писалось как daimon и означало ангелов хранителей или духов помощников, которые помогали формировать характеры людей. Сократ утверждал, что у него был демон, который ему помогал. Демон Сократа говорил ему когда следует держать язык за зубами. Он рассказал о своем демоне во время суда в 399 году до нашей эры. Так что вера в демонов существует довольно давно. Иногда слово daimon пишется как daemon. Это одно и то же.
В то время как daemon — помощник, demon — это злой персонаж из библии. Различия в написании не случайны и видимо так было решено где-то в 16-том веке. Тогда решили, что daemons — хорошие парни, а demons — плохие.
Использовать слово демон (daemon) в вычислительной технике начали в 1963 году. Проект Project MAC (Project on Mathematics and Computation) был разработан в Массачусетском технологическом институте. И именно в этом проекте начали использовать слово демон для обозначения любых программ, которые работают в фоновом режиме, следят за состоянием других процессов и выполняют действия в зависимости от ситуации. Эти программы были названы в честь демона Максвелла.
Демон Максвелла — это результат мысленного эксперимента. В 1871 году Джеймс Клер Максвелл представил себе существо, способное наблюдать и направлять движение отдельных молекул. Целью мысленного эксперимента было показать противоречия во втором законе термодинамики.
Однако есть и другие варианты значения этого слова. Например это может быть аббревиатура от Disk And Executive MONitor. Хотя первоначальные пользователи термина демон не использовали его для этих целей, так что вариант с аббревиатурой, скорее всего неверный.
Теперь вы знаете что такое демоны в понятии Linux. На завершение, обратите внимание, что талисман BSD — демон. Он выбран в честь программных демонов (daemons) но выглядит как злой демон (demon). Имя этого демона Beastie. Точно неизвестно откуда взялось это имя, но есть предположения, оно походит от фразы: BSD. Try it; I did. Если произнести это на английском быстро, получится звук похожий на Beastie. А ещё тризуб Beastie символизирует разветвление (forking) процессов в Linux.
Что такое демон в linux
— Я и есть демон! Слушай, малыш, в моем мире демоном был бы ты, но в текущий момент я в твоем мире, поэтому демон я.
Роберт Асприн, Другой отличный миф
Демонами в мире Unix традиционно называются процессы, которые не взаимодействуют с пользователем напрямую. У процесса-демона нет управляющего терминала и нет, соответственно, пользовательского интерфейса. Для управления демонами приходится использовать другие программы. Само название «демоны» возникло благодаря тому, что многие процессы этого типа большую часть времени проводят в ожидании какого-то события. Когда это событие наступает, демон активизируется (выпрыгивает, как чертик из табакерки), выполняет свою работу и снова засыпает в ожидании события. Следует отметить, что многие демоны, такие как, например, Web-сервер или сервер баз данных, могут отбирать на себя практически все процессорное время и другие ресурсы системы. Такие демоны гораздо больше работают, чем спят.
Вам, уважаемый читатель, вряд ли придется заниматься созданием собственного демона, поскольку круг задач, для которых может понадобиться демон, не так уж и велик. Область применения демонов – создание таких приложений, которые могут, и должны, выполняться без участия пользователя. Обычно это разного рода серверы. Тем не менее, демоны задействуют многие важные элементы системы и понимание принципов работы демонов способствует пониманию принципов работы Unix/Linux в целом (помимо прочего, добрый демон поможет нам изучить некоторые особенности применения сигналов, с которыми мы ранее не сталкивались). В качестве примера демона мы рассмотрим простой (очень простой) сетевой сервер aahzd (надеюсь, что поклонники творчества Асприна меня поймут и простят), способный принимать запросы клиентов и возвращать ответы. Исходный код нашего сервера (вы найдете его на диске в файле aahzd.c) представляет собой доработанный исходный код открытого демонстрационного (простите за невольный каламбур) демона, написанного Давидом Жилье (David Gillies). Те места исходного текста, в которые я внес изменения, помечены в файле aahzd.c комментарием “Note by A.B. . ” Поскольку разговор о демонах вызывает у меня трепет, я не буду долго теоретизировать и сразу перейду к исходному коду (функции main() нашего демона)
volatile sig_atomic_t gGracefulShutdown=0; volatile sig_atomic_t gCaughtHupSignal=0; int gLockFileDesc=-1; int gMasterSocket=-1; const int gaahzdPort=30333; const char *const gLockFilePath = "/var/run/aahzd.pid"; int main(int argc,char *argv[]) < int result; pid_t daemonPID; if (argc >1) < int fd, len; pid_t pid; char pid_buf[16]; if ((fd = open(gLockFilePath, O_RDONLY)) < 0) < perror("Lock file not found. May be the server is not running?"); exit(fd); >len = read(fd, pid_buf, 16); pid_buf[len] = 0; pid = atoi(pid_buf); if(!strcmp(argv[1], "stop")) < kill(pid, SIGUSR1); exit(EXIT_SUCCESS); >if(!strcmp(argv[1], "restart")) < kill(pid, SIGHUP); exit(EXIT_SUCCESS); >printf ("usage %s [stop|restart]\n", argv[0]); exit (EXIT_FAILURE); > if((result = BecomeDaemonProcess(gLockFilePath, "aahzd", LOG_DEBUG, &gLockFileDesc, &daemonPID)) <0) < perror("Failed to become daemon process"); exit(result); >if((result = ConfigureSignalHandlers()) <0) < syslog(LOG_LOCAL0|LOG_INFO, "ConfigureSignalHandlers failed, errno=%d", errno); unlink(gLockFilePath); exit(result); >if((result = BindPassiveSocket(INADDR_ANY, gaahzdPort, &gMasterSocket)) <0) < syslog(LOG_LOCAL0|LOG_INFO, "BindPassiveSocket failed, errno=%d", errno); unlink(gLockFilePath); exit(result); >do < if(AcceptConnections(gMasterSocket)<0) < syslog(LOG_LOCAL0|LOG_INFO,"AcceptConnections failed, errno=%d",errno); unlink(gLockFilePath); exit(result); >if((gGracefulShutdown==1)&&(gCaughtHupSignal==0)) break; gGracefulShutdown=gCaughtHupSignal=0; > while(1); TidyUp(); return 0; >
Сейчас мы пропустим блок операторов if (argc > 1) <. >(мы вернемся к нему позже) и рассмотрим основные этапы работы демона. Функция BecomeDaemonProcess() превращает обычный консольный процесс Linux в процесс-демон. Функция ConfigureSignalHandlers() настраивает обработчики сигналов процесса-демона, а функция BindPassiveSocket() открывает определеный порт TCP/IP для прослушивания входящих запросов. Далее следует цикл, в котором сервер обрабатывает запросы. Многие сетевые серверы, получив запрос, создают дочерний процесс для его обработки. Таким образом достигается возможность параллельной обработки запросов. Некоторые серверы используют для параллельной обработки запросов потоки. Что касается нашего сервера, то из соображений простоты он обрабатывает запросы в последовательном (блокирующем) режиме. Мы ведь не ожидаем, что наш демонстрационный сервер будет получать много запросов, не так ли?
Нормальный выход из цикла обработки запросов происходит при получении процессом сигнала SIGUSER1. После выхода из цикла процесс вызывает функцию TidyUp() и завершает работу. Мы, безусловно, можем завершить процесс-демон, послав ему сигнал SIGKILL (SIGTERM и некоторые другие), но пользовательский сигнал SIGUSER1 гарантирует вежливое завершение нашего демога. «Вежливое завершение» означает, что сервер ответит на текущий запрос перед тем, как завершиться и удалит свой pid- файл.
Рассмотрим теперь подробнее функцию BecomeDaemonProcess(), благодаря которой обычный процесс Linux становится демоном. Мы не будем перепечатывать тексты функций целиком, так как иначе статья будет состоять исключительно из листингов (на самом деле, длина листингов даже превышает пространство, выделенное под статью). По этой причине во время чтения статьи рекомендуется постоянно иметь под рукой прилагаемый исходный текст.
chdir("/");
Мы делаем корневую директорию текущей директорией процесса-демона. Будучи запущен, наш демон может работать вплоть до перезагрузки системы, поэтому его текущая диретокрия должна принадлежать файловой системе, которая не может быть размонтирована. Далее следует вызов lockFD = open(lockFileName, O_RDWR|O_CREAT|O_EXCL, 0644);
Каждый процесс-демон создает так называемый pid-файл (или файл блокировки). Этот файл обычно содержится в директории /var/run и имеет имя daemon.pid, где “daemon” соответствует имени демона. Файл блокировки содержит значение PID процесса демона. Этот файл важен по двум причинам. Во-первых, его наличие позволяет установить, что в системе уже запущен один экземпляр демона. Большинство демонов, включая наш, должны выполняяться не более чем в одном экземпляре (это логично, если учесть, что демоны часто обращаются к неразделяемым ресурсам, таким, как сетевые порты). Завершаясь, процесс-демон удаляет pid-файл, указывая тем самым, что можно запустить другой экземпляр процесса. Однако, работа демона не всегда завершается нормально, и тогда на диске остается pid-файл несуществующего процесса. Это, казалось бы, может стать непреодолимым препятствием для повторного запуска демона, но на самом деле, демоны успешно справляются с такими ситуациями. В процессе запуска демон проверяет наличие на диске pid-файла с соответствующим именем. Если такой файл существует, демон считывает из него значение PID и с помощью функции kill(2) проверяет, существует ли в системе процесс с указанным PID. Если процесс существует, значит, пользователь пытается запустить демон повторно. В этом случае программа выводит соответствующее сообщение и завершается. Если процесса с указанным PID в системе нет, значит pid-файл принадлежал аварийного завершенному демону. В этой ситуации программа обычно советует пользователю удалить pid-файл (ответственность в таких делах всегда лучше переложить на пользователя) и попытаться запустить ее еще раз. Может, конечно, случиться и так, что после аварийного завершения демона на диске останется его pid-файл, а затем какой-то другой процесс получит тот же самый PID, что был у демона. В этой ситуации для вновь запускаемого демона все будет выглядеть так, как будто его копия уже работает в системе, и запустить демон повторно вы не сможете. К счастью, описанная ситуация крайне маловероятна.
Вторая причина, по которой файл блокировки считается полезным, заключается в том, что с помощью этого файла мы можем быстро выяснить PID демона, не прибегая к команде ps.
Далее наш демон вызывает функцию fork(3), которая создает копию его процесса. Родительский процесс при этом завершается:
curPID=fork(); switch(curPID) < case 0: /* мы в дочернем процессе */ break; case -1: /* ошибка fork() - случилось что-то страшное */ fprintf(stderr,"Error: initial fork failed: %s\n", strerror(errno)); return -1; break; default: /* мы в родительском процессе, завершаем его */ exit(0); break; >
Делается это для того чтобы процесс-демон отключился от управляющего терминала. С каждым терминалом Unix связан набор групп процессов, именуемый сессией. В каждый момент времени только одна из групп процессов, входящих в сессию, имеет доступ к терминалу (то есть, может выполнять ввод/вывод с помощью терминала). Эта группа именуется foreground (приоритетной). В каждой сессии есть процесс-родоначальник, который называется лидером сессии. Если процесс-демон запущен с консоли, он, естественно, становится частью приоритетной группы процессов, входящих в сессию соответствующего терминала.
Для того чтобы отключиться от терминала, демон должен начать новую сессию, не связанную с каким-либо терминалом. Для того чтобы демон мог начать новую сессию, он сам не должен быть лидером какой-либо другой сессии. Вызов fork() создает дочерний процесс, который заведомо не является лидером сессии. Далее дочерний процесс, полученный с помощью fork(), начинает новую сессию с помощью вызова функции setsid(2). При этом процесс становится лидером (и единственным участником) новой сессии.
if(setsid()<0) return -1;
Итак, на данном этапе мы имеем процесс, не связанный с каким-либо терминалом. Далее в некоторых руководствах рекомендуется вызвать fork() еще раз, чтобы новый процесс перестал быть лидером новой сессии (в System V лидер сессии может автоматически получить управляющий терминал при некоторых условиях). В Linux в повторном вызове fork() нет необходимости и мы его делать не будем.
Стоит отметить, что теперь наш демон получил новый PID, который мы снова должны записать в pid-файл демона. Мы записываем значение PID в файл в строковом виде (а не как переменную типа pid_t). Делается это для удобства пользователя, чтобы значение PID из pid-файла можно было прочитать с помощью cat. Например:
kill `cat /var/run/aahzd.pid `
Нашему демону удалось разорвать связь с терминалом, с которого он был запущен, но он все еще может быть связан с другими процессами и файловыми системами через файловые дескрипторы, унаследованные от родительских процессов. Для того чтобы разорвать и эту связь, мы закрываем все файловые дескрипторы, открытые в нашем процессе:
numFiles = sysconf(_SC_OPEN_MAX); for(i = numFiles-1; i >= 0; --i)
Функция sysconf() с параметром _SC_OPEN_MAX возвращает максимально возможное количество дескрипторов, которые может открыть наша программа. Мы вызываем функцию close() для каждого дескриптора (независимо от того, открыт он или нет), за исключением дескриптора pid- файла, который должен оставаться открытым.
Во время работы демона дескрипторы стандартных потоков ввода, вывода и ошибок также должны быть открыты, поскольку они необходимы многим функциям стандартной библиотеки. В то же время, эти дескприторы не должны указывать на какие-либо реальные потоки ввода/вывода. Для того, чтобы решить эту задачу, мы закрываем первые три дескриптора, а затем снова открываем их, указывая в качестве имени файла /dev/null:
stdioFD = open("/dev/null", O_RDWR); dup(stdioFD); dup(stdioFD);
Теперь мы можем быть уверены, что демон не получит доступа к какому- либо терминалу. Тем не менее, у демона должна быть возможность выводить куда-то сообщения о своей работе. Традиционно для этого используются файлы журналов (log-файлы). Файлы журналов для демона подобны черным ящикам самолетов. Если в работе демона произошел какой-то сбой, пользователь может проанализировать файл журнала и (при определенном везении) установить причину сбоя. Ничто не мешает нашему демону открыть свой собственный файл журнала, но это не очень удобно. Большинство демонов пользуются услугами утилиты syslog, ведущей журналы множества системных событий. Мы открываем доступ к журналу syslog с помощью функции openlog(3):
openlog(logPrefix, LOG_PID|LOG_CONS|LOG_NDELAY|LOG_NOWAIT, LOG_LOCAL0); (void) setlogmask(LOG_UPTO(logLevel));
Первый параметр функции openlog() – префикс, который будет добавляться к каждой записи в системном журнале. Далее следуют различные опции syslog. Функция setlogmask(3) позволяет установить уровень приоритета сообщений, которые записываются в журнал событий. При вызове функции BecomeDaemonProcess() мы передаем в параметре logLevel значение LOG_DEBUG. В сочетании с макросом LOG_UPTO это означает, что в журнал будут записываться все сообщения с приоритетом, начиная с наивысшего и заканчивая LOG_DEBUG.
Последнее, что нам нужно сделать для «демонизации» процесса – вызывать функцию setpgrp();
Этот вызов создает новую группу процессов, идентификатором которой является идентификатор текущего процесса. На этом работа функции BecomeDaemonProcess() завершается, так как теперь наш процесс стал настоящим демоном.
Функция ConfigureSignalHandlers() настраивает обработчики сигналов. Сигналы, которые получит наш демон, можно разделить на три группы: игнорируемые, «фатальные» и обрабатываемые. Вызывая функцию signal(SIGUSR2, SIG_IGN);
мы указываем, что наш демон должен игнорировать сигнал SIGUSR2. Аналогично мы поступаем с сигналами SIGPIPE, SIGALRM, SIGTSTP SIGPROF, SIGCHLD. Сигналы SIGQUIT, SIGILL, SIGTRAP, SIGABRT, SIGIOT, SIGBUS, SIGFPE, SIGSEGV, SIGSTKFLT, SIGCONT, SIGPWR и SIGSYS относятся к категории «фатальных». Мы не можем их игнорировать, но и продолжать выполнение процесса-демона после получения одного из этих сигналов нежелательно. Мы назначаем всем этим сигналам обработчик FatalSigHandler, например:
signal(SIGQUIT, FatalSigHandler);
Функция-обработчик FatalSigHandler() записывает в журнал событий информацию о полученном сигнале, и затем завершает процесс, вызвав перед этим функции closelog() и TidyUp(), которые высвобождают все занятые процессом ресурсы:
void FatalSigHandler(int sig)
На те три сигнала, которые относятся к категории обрабатываемых, – SIGTERM, SIGUSR1 и SIGHUP, демон реагирует по-разному:
sigtermSA.sa_handler = TermHandler; sigemptyset(&sigtermSA.sa_mask); sigtermSA.sa_flags = 0; sigaction(SIGTERM, &sigtermSA, NULL); sigusr1SA.sa_handler = Usr1Handler; sigemptyset(&sigusr1SA.sa_mask); sigusr1SA.sa_flags = 0; sigaction(SIGUSR1, &sigusr1SA, NULL); sighupSA.sa_handler = HupHandler; sigemptyset(&sighupSA.sa_mask); sighupSA.sa_flags = 0; sigaction(SIGHUP, &sighupSA, NULL);
Обработчик TermHandler() вызывает функцию TidyUp() и завершает процесс. Обработчик Usr1Handler() делает в системном журнале запись о вежливом завершении процесса и присваивает переменной gGracefulShutdown значение 1 (что, как вы помните, приводит к выходу из цикла обработки запросов, когда цикл будет готов к этому). Обработчик сигнала HupHandler() также делает запись в системном журнале, после чего присваивает значение 1 переменным gGracefulShutdown и gCaughtHupSignal. В реальной жизни получение сигнала SIGHUP приводит к перезапуску демона, который сопровождается повторным прочтением файла конфигурации (который обычно читается демоном именно во время запуска) и переустановкой значений записанных в нем параметров. Именно необходимость прочесть повторно файл конфигурации является наиболее частой причиной перезапуска демонов. У нашего демона файла конфигурации нет, так что в процессе перезапуска делать ему особенно нечего.
Обратите внимание на тип переменных gGracefulShutdown и gCaughtHupSignal. С типом sig_atomic_t мы раньше не встречались. Применение этого типа гарантирует, что чтение и запись данных в переменные gGracefulShutdown и gCaughtHupSignal будет выполняться атомарно, одной инструкцией процессора, которая не может быть прервана. Атомарность при работе с переменными gGracefulShutdown и gCaughtHupSignal важна потоуму, что к ним могут одновременно получить доступ и обработчики сигналов, и главная функция программы. По этой же причине мы помечаем указанные переменные ключевым словом volatile.
Функция BindPassiveSocket() открывает для прослушивания порт сервера (в нашем случае это порт 30333) на всех доступных сетевых интерфейсах и возвращает соответствующий сокет:
int BindPassiveSocket(const int portNum, int *const boundSocket)
Тем, кто читал статью этой серии, посвященную сокетам, должно быть понятно, что здесь происходит. Отметим только одну интересную деталь. Если предыдущий, уже закрытый, сокет, связанный с данным портом, находится в состоянии TIME_WAIT, между закрытием старого и открытием нового сокета может произойти задержка, равная двум периодам жизни сегмента (задержка может составлять до двух минут). Для того, чтобы при повторном запуске демона нам не пришлось ждать, мы используем функцию setsockopt() с параметром SO_REUSEADDR.
Функция AcceptConnections() обрабатывает запросы последовательно, используя блокирующий вызов accept():
int AcceptConnections(const int master) < int proceed = 1, slave, retval = 0; struct sockaddr_in client; socklen_t clilen; while((proceed==1)&&(gGracefulShutdown==0)) < clilen = sizeof(client); slave = accept(master,(struct sockaddr *)&client,&clilen); if(slave<0) < /* ошибка accept() */ if(errno == EINTR) continue; syslog(LOG_LOCAL0|LOG_INFO,"accept() failed: %m\n"); proceed = 0; retval = -1; >else < retval = HandleConnection(slave); if(retval) proceed = 0; >close(slave); > return retval; >
Это не лучший образ поведения демона, но если мы начнем описывать параллельную обработку запросов, редакция не выдержит. Переменная proceed, совместно с переменной gGracefulShutdown, указывает, должна ли программа продолжать обрабатывать запросы. Если очередной вызов connect() или HandleConnection() вернул сообщение об ошибке, этой переменной присваивается 0 и обработка запросов прекращается. Новый сокет, полученный в результате вызовы accept(), передается функции HandleConnection().
int HandleConnection(const int slave)
Функция HandleConnection() считывает переданную клиентом строку и тут же возвращает ее клиенту. Затем функция AcceptConnections() закрывает соединение, открытое в результате вызова accept(). Функции ReadLine() и WriteToSocket() тривиальны, и рассматривать их мы не будем. Если где-то в цепочке вызовов AcceptConnections(), HandleConnection(), ReadLine() и WriteToSocket() возникла ошибка, информация об ошибке будет передаваться вверх по цепочке до тех пор, пока не достигнет функции main(). В функции main() эта информация приведет к немедленному завершению работы демона с соответствующей записью в журнал системных сообщений.
Рассмотрим, наконец, функцию TidyUp(), к которой обращаются многие функции сервера перед тем, как завершить его работу.
void TidyUp(void) < if(gLockFileDesc!=-1) < close(gLockFileDesc); unlink(gLockFilePath); gLockFileDesc=-1; >if(gMasterSocket!=-1) < close(gMasterSocket); gMasterSocket=-1; >>
Задача функции TidyUp() – «прибрать мусор» за процессом-демоном. В принципе, без этой функции можно обойтись, так как после завершения процесса система сама закроет все его дескрипторы, но правила хорошего тона требуют явного высвобождения всех ресурсов, выделенных явным образом.
Если вы скомпилируете программу-демон с помощью команды
gcc aahzd.c -o aahzd
То сможете запустить демон командой
# aahzd
Поскольку демон нуждается в доступе к директории /var/run, запускать его нужно в режиме root. Сразу после запуска программы вы снова увидите приглашение командной строки, что для демонов совершенно нормально. Если бы сервер aahzd выполнял что-нибудь полезное, команду его запуска можно было бы прописать в одном из сценариев запуска системы в директории /etc/init.d, но мы этого делать не будем. После того как сервер запущен, вы можете дать команду
telnet 127.0.0.1 30333
В результате будет установлено соединение с сервером. Напечатайте какую-нибудь последовательность символов в консоли telnet и нажмите «Вввод». В качестве ответа сервер возвратит напечатанную строку и закроет соединение.
Вернемся теперь к начальным строкам функции main(). Хотя мы можем получить PID демона из его pid-файла и управлять демоном с помощью команды kill, такой вариант нельзя назвать очень удобным. Часто для управления демоном используется сам исполнимый файл демона, запускаемый со специальными аргументами командной строки. Наш демон понимает две команды: stop (завершение работы демона) и restart (перезапуск). Посмотрим, как поведет себя демон, запущенный с аргументами командной строки. В этом случае в начале программы демон пытается считать значение PID из своего pid-файла. Если открыть pid-файл не удается, значит, скорее всего, демон не запущен, и управляющему режиму просто нечего делать. Если значение PID получено, процесс, управляющий демоном, посылает демону соответствующий сигнал с помощью функции kill().
Демоны не рассчитаны на то, чтобы поучать какую-либо информацию от пользователя. Собственную информацию они передают другим программам либо записывают в журналы системных событий. В следующей статье мы сосредоточимся на программах, которые ведут себя совершенно иначе и рассмотрим консольный ввод-вывод.