Книга: Операционная система UNIX
Демон
Демон
Демоны играют важную роль в работе операционной системы. Достаточно будет сказать, что возможность терминального входа пользователей в систему, доступ по сети, использование системы печати и электронной почты, — все это обеспечивается соответствующими демонами — неинтерактивными программами, составляющими собственные сеансы (и группы) и не принадлежащими ни одному из пользовательских сеансов (групп).
Некоторые демоны работают постоянно, наиболее яркий пример такого демона — процесс init(1M), являющийся прародителем всех прикладных процессов в системе. Другими примерами являются cron(1M), позволяющий запускать программы в определенные моменты времени, inetd(1M) обеспечивающий доступ к сервисам системы из сети, и sendmail(1M), обеспечивающий получение и отправку электронной почты.
При описании взаимодействия процессов с терминалом и пользователем в разделе "Группы и сеансы", отмечалось особое место демонов, которые не имеют управляющего терминала. Теперь в отношении демонов можно сформулировать ряд правил, определяющих их нормальное функционирование, которые необходимо учитывать при разработке таких программ:
1. Демон не должен реагировать на сигналы управления заданиями, посылаемые ему при попытке операций ввода/вывода с управляющим терминалом. Начиная с некоторого времени, демон снимает ассоциацию с управляющим терминалом, но на начальном этапе запуска ему может потребоваться вывести то или иное сообщение на экран.
2. Необходимо закрыть все открытые файлы (файловые дескрипторы), особенно стандартные потоки ввода/вывода. Многие из этих файлов представляют собой терминальные устройства, которые должны быть закрыты, например, при выходе пользователя из системы. Предполагается, что демон остается работать и после того, как пользователь "покинул" UNIX.
3. Необходимо снять его ассоциацию с группой процессов и управляющим терминалом. Это позволит демону избавиться от сигналов, генерируемых терминалом (SIGINT
или SIGHUP
), например, при нажатии определенных клавиш или выходе пользователя из системы.
4. Сообщения о работе демона следует направлять в специальный журнал с помощью функции syslog(3), — это наиболее корректный способ передачи сообщений от демона.
5. Необходимо изменить текущий каталог на корневой. Если этого не сделать, а текущий каталог, допустим, находится на примонтированной файловой системе, последнюю нельзя будет размонтировать. Самым надежным выбором является корневой каталог, всегда принадлежащий корневой файловой системе.
Приведем скелет программы-демона:
#include <stdio.h>
[28]
#include <syslog.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/resource.h>
main(int argc, char **argv) {
int fd;
struct rlimit flim;
/* Если родительский процесс — init, можно не беспокоиться
за терминальные сигналы. Если нет — необходимо игнорировать
сигналы, связанные с вводом/выводом на терминал
фонового процесса: SIGTTOU, SIGTTIN, SIGTSTP */
if (getppid() != 1) {
signal(SIGTTOU, SIG_IGN);
signal(SIGTTIN, SIG_IGN);
signal(SIGTSTP, SIG_IGN);
/* Теперь необходимо организовать собственную группу и сеанс,
не имеющие управляющего терминала. Однако лидером группы и
сеанса может стать процесс, если он еще не является лидером.
Поскольку предыстория запуска данной программы неизвестна,
необходима гарантия, что наш процесс не является лидером.
Для этого порождаем дочерний процесс. Т.к. его PID уникален,
то ни группы, ни сеанса с таким идентификатором не существует,
а значит нет и лидера. При этом родительский процесс
немедленно завершает выполнение, поскольку он уже не нужен.
Существует еще одна причина необходимости порождения
дочернего процесса. Если демон был запущен из командной строки
командного интерпретатора shell не в фоновом режиме,
последний будет ожидать завершения выполнения демона,
и таким образом, терминал будет заблокирован.
Порождая процесс и завершая выполнение родителя,
имитируем для командного интерпретатора завершение
работы демона, после чего shell выведет свое приглашение */
if (fork () !=0)
exit(0); /* Родитель заканчивает работу */
/* Дочерний процесс с помощью системного вызова
становится лидером новой группы, сеанса и не имеет
ассоциированного терминала */
}
/* Теперь необходимо закрыть открытые файлы. Закроем
все возможные файловые дескрипторы. Максимальное число
открытых файлов получим с помощью функции getrlimit */
getrlimit(RLIMIT_NOFILE, &flim);
for (fd = 0; fd < flim.rlim_max; fd++)
close(fd);
/* Сменим текущий каталог на корневой */
chdir("/");
/* Заявим о себе в системном журнале. Для этого сначала
установим опции ведения журнала: каждая запись будет
предваряться идентификатором PID демона, при невозможности
записи в журнал сообщения будут выводиться на консоль,
источник сообщений определим как "системный демон"
(см. комментарии к функциям ведения журнала ниже). */
openlog("Скелет демона" , LOG_PID | LOG_CONS, LOG_DAEMON);
/* Отметимся */
syslog(LOG_INFO, "Демон начал плодотворную работу...");
closelog();
/* Далее следует текст программы, реализующий полезные функции
демона. Эта часть предоставляется читателю для собственной
разработки. */
...
}
В программе использовалось еще не обсуждавшаяся возможность системного журнала сообщений выполняющихся программ. Функцией генерации сообщений является syslog(3), отправляющая сообщение демону системного журнала syslogd(1M), который в свою очередь либо дописывает сообщения в системный журнал, либо выводит на их консоль, либо перенаправляет в соответствии со списком пользователей данной или удаленной системы. Конкретный пункт назначения определяется конфигурационным файлом (/etc/syslog.conf). Функция имеет определение:
#include <syslog.h>
void syslog(int priority, char *logstring, /* параметры*/...);
Каждому сообщению logstring
назначается приоритет, указанный параметром priority
. Возможные значения этого параметра включают:
LOG_EMERG |
Идентифицирует состояние "паники" в системе. Обычно рассылается всем пользователям. |
LOG_ALERT |
Идентифицирует ненормальное состояние, которое должно быть исправлено немедленно, например, нарушение целостности системной базы данных. |
LOG_CRIT |
Идентифицирует критическое событие, например, ошибку дискового устройства. |
LOG_ERR |
Идентифицирует различные ошибки. |
LOG_WARNING |
Идентифицирует предупреждения. |
LOG_NOTICE |
Идентифицирует события, которые не являются ошибками, но требуют внимания. |
LOG_INFO |
Идентифицирует информационные сообщения, как, например, использованное в приведенной программе. |
LOG_DEBUG |
Идентифицирует сообщение, обычно используемое только при отладке программы. |
Последний тип сообщений подсказывает еще одну возможность использования системного журнала — для отладки программ, особенно неинтерактивных.
Строка logstring
может включать элементы форматирования, такие же, как и в функции printf(3), с одним дополнительным выражением %m
, которое заменяется сообщением, соответствующим ошибке errno
. При этом может осуществляться вывод значений дополнительных параметров.
Функция openlog(3) позволяет определить ряд опций ведения журнала. Она имеет следующее определение:
void openlog(char *ident, int logopt, int facility);
Строка ident
будет предшествовать каждому сообщению программы. Аргумент logopt
задает дополнительные опции, в том числе:
LOG_PID |
Позволяет указывать идентификатор процесса в каждом сообщении. Эта опция полезна при журналировании нескольких демонов с одним и тем же значением ident, например, когда демоны порождаются вызовом fork(2). |
LOG_CONS |
Позволяет выводить сообщения на консоль при невозможности записи в журнал. |
Наконец, аргумент facility
позволяет определить источник сообщений:
LOG_KERN |
Указывает, что сообщения отправляются ядром. |
LOG_USER |
Указывает, что сообщения отправлены прикладным процессом (используется по умолчанию). |
LOG_MAIL |
Указывает, что инициатором сообщений является система электронной почты. |
LOG_DAEMON |
Указывает, что инициатором сообщений является системный демон. |
LOG_NEWS |
Указывает, что инициатором сообщений является система телеконференций USENET. |
LOG_CRON |
Указывает, что инициатором сообщений является система cron(1). |
Закончив работу с журналом, следует аккуратно закрыть его с помощью функции closelog(3):
void closelog(void);
- 5.14 МОНТИРОВАНИЕ И ДЕМОНТИРОВАНИЕ ФАЙЛОВЫХ СИСТЕМ
- Демонтаж и установка МП
- Демонтаж
- 5.4. Демон inetd
- Почему мы настаиваем на том, чтобы каждый спринт заканчивался демонстрацией
- 7.2.6.3. Системные демоны и традиционные сигналы
- Пример приложения Pocket PC, демонстрирующий работу средств контроля запуска событий
- Демонстрация приемов работы на конкретном примере
- 2. Плата за демонстрацию и рекламу товара
- Листинг 13.6. Код формы, демонстрирующий загрузку встроенных ресурсов
- Листинг 13.5. код формы, демонстрирующий использование прозрачности '
- 8.1. Монтирование и демонтирование файловых систем