Как заставить компьютер в указанное время выполнить какую-то задачу без вашего непосредственного участия? Можно ли оставить ему «домашнее задание», а самому заняться другими делами?

Демон, страдающий бессонницей

Остановимся на случае, когда компьютер работает под управлением операционной системы Linux; для решения поставленной задачи будем использовать только штатные системные средства — демоны crond, anacron и atd, и стандартные утилиты для управления ими.

В дистрибутив Red Hat 7.3 входит вариант демона crond, названный Vixie Cron по имени его разработчика Пола Викси. Этот системный демон предназначен для выполнения регулярно повторяющихся заданий и обычно запускается как системная служба в процессе начальной загрузки системы и остается активным, пока система не выключена. Сразу после старта демон просматривает каталоги /var/spool/cron и /etc/cron.d, а также файл /etc/crontab в поисках заданий, которые нужно выполнять. В дальнейшем crond просыпается каждую минуту, выполняет предписанные ему задания и вновь засыпает до начала следующей минуты.

Для формирования домашнего задания для cron лучше всего воспользоваться командой crontab. Если выполнить ее с флагом -l:

[user]$ crontab -l

будет выведен текущий список заданий. Если заданий нет, появляется сообщение «no crontab for user». Сформулировать задание довольно просто, но, прежде чем пытаться это сделать, установите в качестве значения переменной окружения EDITOR указание на ваш любимый текстовый редактор. В противном случае будет вызван vi. Я обычно выполняю команду:

[user]$ export EDITOR=mcedit

после чего получаю возможность использовать для редактирования списка заданий привычный редактор CoolEdit из пакета Midnight Commander.

После задания значения переменной EDITOR можно выполнить команду:

[user]$ crontab -e

вызывающую указанный редактор. При первом запуске такой команды откроется пустое окно, в котором следует сформулировать задание для cron. А что мешает просто запустить любимый редактор, открыть для редактирования нужный файл и внести в него необходимые команды? Во-первых, файлы, которые хранят задания для cron, принадлежат пользователю root и защищены от модификации простыми пользователями. Команда crontab запускается от имени root (для нее установлен, так называемый бит setuid) и имеет доступ к этим файлам. (Конечно, если речь идет о вашем ПК, где вы имеете все права, вам законы не писаны, но лучше все же придерживаться принятых правил игры.) Во-вторых, записи в crontab-файлах должны подчиняться определенным стандартам, быть формализованы, чтобы crond мог их правильно интерпретировать. Команда crontab после того, как вы сохранили вновь отредактированный файл, производит его синтаксический анализ, и, если вы сделали какую-то ошибку, предлагает вернуться к его редактированию. Конечно, crontab не может сформулировать за вас задание для cron, так что правила написания заданий вам необходимо знать.

Каждая строка crontab-файла (кроме строк комментариев со знаком # в первой позиции) либо устанавливает значение некоторой переменной, либо представляет отдельное задание. Строка задания состоит из шести полей, разделенных пробелами. Первые пять полей отведены для указания времени выполнения задания. В таблице 1 представлены значения, которые можно придавать этим полям.

Таблица 1. Назначение полей в строках заданий crontab

В каждом из полей вместо простого числового значения можно прописать:

  • список возможных значений, разделенных запятыми (в списках можно использовать только числа, имена не допускаются);
  • интервал значений (например, 1-3);
  • звездочку (*), обозначающую любое из допустимых значений для данного поля.

Есть также возможность указать, что данное задание должно выполняться только в каждый n-ый час (минуту, день или месяц), для чего в нужном поле записывают примерно следующее: «*/n» (кавычки, конечно, нужно опустить, поставив вместо n конкретное число). Эти варианты записи времени выполнения заданий можно комбинировать, но об этом лучше прочитать на странице справочника crontab(5).

Подчеркнем, что для указания дня отведено два поля: третье и пятое. Если в обоих полях заданы значения, отличные от *, то задание будет выполняться в те дни, когда хотя бы одно из значений дня совпадает c текущим. Например, если в третьем поле стоит 1,15, а в пятом — 5 (или FRI), то задание будет выполняться по первым и пятнадцатым числам каждого месяца, а также каждую пятницу (конечно, если в поле месяцев будет стоять *).

Следующее поле содержит подлежащую выполнению командную строку shell. Считается, что поле команды продолжается до конца строки и может содержать пробелы и символы табуляции; заключать эту командную строку в кавычки не требуется.

Задание переменных окружения для cron рассмотрим на примере переменной MAILTO. Надо сказать, что по умолчанию после выполнения каждой строки crontab-файла cron формирует отчет о выполнении задания. Содержанием такого отчета является вывод исполнявшихся команд. Если не задавать переменную MAILTO, то сообщение отправляется тому пользователю, который сформировал задание. Если вы хотите, чтобы сообщения отправлялись не вам, а пользователю с именем fred, то в crontab-файле надо записать строку вида:

MAILTO=«fred»

Если они не нужны и вам, то установите переменную MAILTO в нуль:

MAILTO=«»

Несколько переменных окружения устанавливается автоматически при запуске cron.

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

Если ошибок не было, вы увидите только одну строку:

[user] $ crontab -e
crontab: installing new crontab

В отличие от других процессов-демонов, которые требуют перезапуска после редактирования их конфигурационных файлов, перезапускать процесс crond после того, как пользователь задал новое задание, не требуется. Просыпаясь ежеминутно, cron проверяет время модификации crontab-файлов и перечитывает те файлы заданий, которые изменялись в последнее время. Поэтому не более чем через минуту ваши задания будут «приняты к исполнению».

Пользоваться услугами crond могут все пользователи, зарегистрированные в системе. Правда, суперпользователь может ограничить эту возможность, либо прописав имена некоторых пользователей в файл /etc/cron.deny, либо разрешив использовать cron лишь определенным пользователям, имена которых перечислены в файле /etc/cron.allow.

Где хранятся crontab-файлы?

До сих пор преднамеренно не называлось имя конкретного crontab-файла, который редактируется по команде crontab -e. Дело в том, что одного такого файла нет; в Red Hat Linux имеется целый каталог /var/spool/cron, в котором хранятся crontab-файлы для всех пользователей, включая root. Каждый такой файл имеет имя, совпадающее с регистрационным именем пользователя, по которому процесс cron определяет, какой идентификатор UID надо использовать при выполнении команд из этого файла. Владельцем всех этих файлов является пользователь root.

Кроме личных crontab-файлов отдельных пользователей существуют также общесистемные. Один из них, /etc/crontab, находится в каталоге /etc, остальные — в каталоге /etc/cron.d. К этим файлам нет доступа через команду crontab; редактировать их может только суперпользователь. Структура записей в таких файлах тоже несколько отличается от описанной выше: в строках заданий используется дополнительное поле, расположенное перед полем команды. В этом дополнительном поле суперпользователь задает имя пользователя, чей идентификатор UID будет применяться при запуске соответствующего задания. При установке дистрибутива Red Hat создается системный файл /etc/crontab:

SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
HOME=/
# run-parts
01 * * * * root run-parts /etc/cron.hourly
02 4 * * * root run-parts /etc/cron.daily
22 4 * * 0 root run-parts /etc/cron.weekly
42 4 1 * * root run-parts /etc/cron.monthly

Команда run-parts служит для запуска всех скриптов из каталога, указанного в виде параметра. По большей части эти скрипты выполняют функции по обслуживанию системы: удаляют ненужные временные файлы, присматривают за быстрорастущими файлами протоколов в каталоге /var/log и, при необходимости, очищают их и т.п.

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

«Сделал работу и ухожу»

Если некоторое задание не было выполнено демоном crond в указанное время, то процесс crond не выполняет такую команду позже, поскольку информация о невыполнении задания ему не поступает. Для некоторых системных заданий это недопустимо. Проблему позволяет решить другой системный демон, anacron. При запуске он просматривает свой конфигурационный файл (обычно /etc/anacrontab), где для каждого задания указывается периодичность его выполнения в сутках. Далее anacron проверяет, выполнялось ли задание в течение последних n дней. Если нет, запускает на выполнение команду, указанную в строке задания. При этом выполнение команды может осуществляться с некоей задержкой, величина которой (в минутах) должна быть указана в строке задания. После завершения задания anacron записывает дату выполнения в специальный файл, содержащий записи о времени последнего запуска задания, чтобы знать, когда надо выполнять его снова. Эти файлы сохраняются в каталоге /var/spool/anacron. В файл записывается только дата — часы и минуты не запоминаются. После выполнения каждого задания anacron посылает сообщение о выполнении системному демону протоколирования syslogd, а после завершения всех заданий из конфигурационного файла, заканчивает работу.

Конфигурационный файл /etc/anacrontab может содержать строки трех типов: строки описания заданий, строки задания переменных окружения и строки комментариев. Строка описания заданий имеет формат:

Период   задержка
   идентификатор_задания   команда

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

Приведем в качестве примера файл /etc/anacrontab из стандартной установки дистрибутива Red Hat:

# /etc/anacrontab: configuration file for anacron
# See anacron(8) and anacrontab(5) for details.
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:
/sbin:/bin:/usr/sbin:/usr/bin
# These entries are useful for
 a Red Hat Linux system.
1   5   cron.daily
   run-parts /etc/cron.daily
7   10   cron.weekly
   run-parts /etc/cron.weekly
30   15   cron.monthly
   run-parts /etc/cron.monthly

Как видите, в Red Hat утилита anacron «подстраховывает» демон cron, запуская периодические задания cron, если тот почему-либо их не запускал. Благодаря этому, в частности, скрипт logrotate выполняется регулярно, несмотря на то, что демоном crond он не запускается из-за выключения компьютера на ночь. Среди скриптов, регулярно запускаемых cron (каталоги /etc/cron.daily, /etc/cron.weekly и /etc/cron.monthly), можно найти скрипт 0anacron, который заботится о том, чтобы обновить записи о времени последнего выполнения заданий, порученных обоим демонам. Это позволяет исключить их повторное выполнение anacron.

При желании можно сделать так, чтобы cron, в свою очередь, «подстраховывал» anacron, периодически запуская его (правда, проще поручить периодические задания самому cron).

Никаких утилит, специально предназначенных для ввода новых заданий демону anacron, не предусмотрено. Такие задания может давать только суперпользователь путем прямого редактирования файла /etc/anacrontab. По-видимому, основное назначение этого демона — выполнять какие-то работы по обслуживанию системы после периодов долгого простоя.

«Жду ваших указаний»

А что, если нужно заставить компьютер выполнить какое-то задание или работу только один раз, например, выключиться в полночь? Для этого в Linux имеется еще один демон — atd, который, как и crond, постоянно находится «на посту». В этом можно убедиться, выполнив команду:

[user] $ ps -ax | grep atd

Для того чтобы задать работу этому демону, применяются команды at и batch.

Команда at в простейшем случае запускается с единственным параметром — временем выполнения задания:

[user] $ at TIME

После этого появляется предупреждение:

warning: commands will
 be executed using (in order)
 a) $SHELL b) login shell c) /bin/sh
at>

и программа ожидает ввода задания. В качестве задания может использоваться любая команда shell, в том числе скрипты. После завершения ввода команды надо нажать комбинацию клавиш [Ctrl-D]. В ответ будет выдано сообщение о том, что задание принято под таким-то номером:

job 4 at 2002-09-26 12:15

Просмотреть очередь ждущих своего времени заданий можно с помощью команды atq. Увы, к сожалению, выводятся только номера ожидающих выполнения работ и время их выполнения; содержание задания, т. е. вызываемая команда, не выводится. Если вы не суперпользователь, вам будут показаны только ваши задания; суперпользователь видит список заданий, введенных всеми пользователями системы. С помощью команды atrm можно удалить задание из этой очереди, указав в качестве параметра его номер.

Рассмотрим правила формирования параметра TIME для команды at, устанавливающие время выполнения задания. В простейшем варианте указывается только час и минута запуска задания, разделенные двоеточием: «hh:mm». Если сегодня указанное время уже прошло, задание будет выполняться завтра. Можно также указать дату выполнения задания в формате MMDDYY, или MM/DD/YY, или DD.MM.YY. Допускается использовать название месяца с числовым указанием дня и необязательным указанием года. Можно также указать программе at, что выполнение задания нужно повторить несколько раз. Для этого после указания времени добавляют количество повторений с указанием, через какой интервал (час, день, неделя или месяц) надо повторить задание. Например, по команде at 10:00PM + 3 days задание будет выполняться в 10 вечера сегодня и еще в течение 3 дней в то же время. Итак, вариантов указания времени выполнения задания существует множество, их полная спецификация приведена в файле /usr/sharedoc/at-3.1.8/timespec (цифры 3.1.8 обозначают версию утилиты at).

Как и в случае с демоном crond, суперпользователь может лишить некоторых пользователей права запускать команду at, прописав их имена в специальный файл /etc/at.deny, либо же разрешить использовать at только тем пользователям, имена которых перечислены в файле /etc/at.allow. При этом shell сначала ищет файл /etc/at.allow и, если он есть, проверяет наличие имени в нем. Второй файл уже не анализируется. Если же файла /etc/at.allow не существует, проверяется /etc/at.deny, и вам разрешается выполнить at, если ваше имя в этом файле не встречается. Если ни того, ни другого файла не существует, программу at может запускать только суперпользователь. В Red Hat Linux по умолчанию создается пустой файл /etc/at.deny, т. е. давать задания демону atd могут все пользователи.

Кроме выполнения заданий в указанное время демон atd может выполнять какие-то работы в периоды низкой загруженности системы. Задания на этот случай формулируются с помощью утилиты batch и ждут своего часа в очереди. Когда загрузка системы снизится до уровня, установленного параметром -l (указывается при запуске atd, по умолчанию равен 0.8), задания из очереди запускаются на выполнение.

Какие задачи поручить демонам?

Самым очевидным примером практического использования демона crond является автоматическая выдача напоминаний о приближении каких-либо важных дат или событий. Проще всего организовать такую процедуру с помощью почтового агента (обычно он постоянно запущен на включенном компьютере). Например, почтовый агент kmail из KDE позволяет просматривать сообщения из нескольких «почтовых ящиков». Поэтому достаточно настроить его еще и на получение предназначенных вам писем из почтовой системы на локальном компьютере, указать cron, когда надо прислать напоминание, и вы будете вовремя оповещены о приближении любого важного для вас события.

Как говорилось выше, в стандартной установке Red Hat Linux демоны crond и anacron выполняют некоторые служебные функции. Характерным примером таких задач является задача обслуживания файлов системных протоколов. Как известно, в Unix постоянно ведется несколько протоколов работы, в которых фиксируются действия пользователей и ядра вместе с другими программами и демонами. И рассматриваемые в статье демоны тоже вносят свои записи в системные протоколы. Отвечает за ведение протоколов системный демон syslogd, причем создаваемые им протоколы быстро растут в объеме и, если их своевременно не чистить, могут заполнить все свободное пространство на диске. В стандартной конфигурации Red Hat Linux за ростом протоколов следит скрипт logrotate, расположенный в каталоге /etc/cron.daily. При превышении объема файла протокола некоторого заданного уровня старый файл переименовывается и открывается новый, а считающиеся уже ненужными файлы уничтожаются.

В [1] приведено еще несколько примеров использования демона crond для решения системных задач наподобие:

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

Приведу еще один пример практического использования демона crond, причем пример, характерный как раз для домашнего компьютера. Рассмотрим обычную житейскую ситуацию: вы набираете на домашнем компьютере какой-то текст или корректируете исходные тексты программы, и в это время отключается электроэнергия. Покупка источника бесперебойного питания конечно не предусматривалась вашим семейным бюджетом, а сохранять результаты работы каждые 15 минут вы тоже, в азарте работы, не удосужились. Но даже если как раз перед отключением компьютера вы сохранили результаты работы и даже вышли из программы редактирования, после восстановления питания последние изменения могут быть потеряны. Это в DOS сохранение результатов редактирования приводит к тому, что они тут же записываются на диск, а в Linux относительно медленные операции чтения/записи на диск кэшируются в оперативной памяти. Это существенно повышает общую производительность системы, но, если система почему-либо рушится, такой подход приводит к тому, что вы потеряете часть результатов своей работы. Вот тут на помощь и может прийти cron [2].

Напомню, что в Linux имеется специальная команда sync, предназначенная для принудительного «сброса» содержимого временных буферов памяти на диск. Давайте будем выполнять ее в автоматическом режиме каждые 15 (или, скажем, 30) минут. Для этого достаточно прописать в crontab-файл строку следующего вида:

0,30 * * * * date; sync; echo
 «запись данных на диск»

А чтобы сообщения о выполнении этой операции не переполняли почтовый ящик, отправляйте их «в никуда». Конечно, и самому не надо забывать нажимать кнопку «Сохранить на диске», иначе никакой cron не спасет от потери данных при неожиданном выключении питания.

Заключение

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

Литература
  1. Э. Немет, Г. Снайдер, С. Сибасс, Т. Хейн. "UNIX: руководство системного администратора". К.: BHV, 1999
  2. Michael Keller, Take Command: cron: Job Scheduler. Linux Journal, 1999, September

Виктор Костромин (kos@nbrt.kazan.su) — независимый эксперт.