Опубликована в журнале "Системный администратор", апрель 2004
Способность операционной системы выполнять задачи в заданное время (по
расписанию) может сделать администрирование существенно проще и
эффективнее, а в ряде случаев такая способность просто необходима. В
данной статье рассмотрены средства отложенной работы и работы по
расписанию, имеющиеся во FreeBSD (на примере FreeBSD 5.1), однако
большинство описанных здесь функций будут доступны Вам и в других ОС
семейства Unix, включая Linux. Особое внимание уделено механизмам
работы, знание которых необходимо для эффективного поиска возможных
проблем.
Прежде всего, рассмотрим утилиту, которая позволяет однократно
выполнить ту или иную команду в заданное время. Это команда at,
простейший синтаксис которой выглядит следующим образом:
# at time
Здесь time - время, когда задача должна быть выполнена. Перечень
команд, составляющих задачу, считывается со стандартного ввода (как
правило, это ввод с клавиатуры, завершаемый символом Ctrl-D).
Например:
# at 11:00pm
who
last -s -20
df
^D
Этот ввод приведет к тому, что в 23:00 (сегодня, если дело происходит
утром, и завтра, если после обеда) последовательно будут выполнены
команды who, last и df, и их вывод будет отправлен на электронный
адрес пользователя.
Команда предоставляет пользователю большую свободу в выборе формата
времени:
Формат времени Примеры Описание
HH[:]MM[pm|am] 1405, 14:05, 2:05pm Команда будет выполнена в 14:05
текущего или следующего дня
midnight midnight Команда выполнится в полночь
noon noon Команда выполнится в полдень
teatime teatime Команда выполнится в 16:00
+N Unit +3 minutes Выполнить через 3 минуты
+3 hours Через 3 часа
+3 days Через 3 дня
+3 weeks Через 3 недели
+3 month Через 3 месяца
+3 years Через 3 года
Таким образом, существует возможность задать время как относительно
момента подачи команды at, так и абсолютно. В последнем случае оно
может быть дополнено указанием конкретной даты, форматы записи которой
не менее разнообразны. Так, дата "12 февраля 2005 года" может быть
записана одним из следующих способов: 12.02.2005, 12.02.05,
12/02/2005, 12/02/05, Feb 12 2005: Также вместо конкретной даты можно
использовать ключевое слово tomorrow, указывающее, что задание должно
быть исполнено в указанное время завтра. При необходимости указать,
что задание должно быть выполнено именно сегодня, можно использовать
дополнение today. Естественно, при попытке задать уже прошедшее время
Вы получите соответствующее сообщение об ошибке.
Кроме того, время может быть задано в формате POSIX, для чего следует
использовать ключ -t:
# at -t [[CC]YY]MMDDhhmm[.SS]
# at -t 12011530 // команда выполнится 1 декабря этого года в 15:30
По умолчанию команду at может выполнять только суперпользователь root.
Чтобы дать такое право другим пользователям, их следует перечислить в
файле /var/at/at.allow. Если файл at.allow в этой директории
отсутствует, но есть файл at.deny, то команду at смогут запускать все
пользователи, кроме перечисленных в at.deny. Обратите внимание, что
имена пользователей в этих файлах должны начинаться строго с первой
позиции и обязательно завершаться символом перевода строки даже для
последней строчки.
Из дополнительных опций команды at перечислим следующие:
-f file - задает имя файла, содержимое которого будет воспринято как
задание. Если опция не указана, задания считываются со стандартного
ввода (до символа Ctrl-D). Например:
# at -f myjobs +2 minutes
В результате через 2 минуты будет выполнена последовательность команд,
перечисленных в файле myjobs. Команды в этом файле задаются по одной
на каждой строке (так же, как и при вводе с клавиатуры). Результат
также будет направлен по электронной почте.
-m - заставляет отсылать письмо о выполнении задания пользователю
(без этой опции письмо отсылается только в том случае, если результат
выполнения задания отправляется на стандартный вывод).
-q queue - позволяет поместить задание в указанную очередь, которая
может быть задана одной буквой латинского алфавита (a-z, A-Z). По
умолчанию задание помещается в очередь "c" для at, и в "E" для batch
(см. далее). Помимо удобства работы с большим числом отложенных
заданий, различные очереди позволяют управлять приоритетом (nice) их
выполнения - чем дальше буква, соответствующая очереди, находится от
начала алфавита, тем большее значение nice, то есть меньший приоритет,
получат задачи, стоящие в этой очереди. Очереди, обозначенные буквами
верхнего регистра, будут отрабатываться только в том случае, если
загрузка системы позволяет это сделать (см. далее описание ключа -b
(batch)), и после того, как будут запущены задания из очередей,
обозначенных строчными буквами.
-l [-q queue] - выводит список заданий, находящихся в очереди. Можно
использовать также псевдоним atq, выполняющий те же функции. Если
команду atq выполняет суперпользователь, то выводятся все задания,
находящиеся в очереди. Иначе - только задания, принадлежащие текущему
пользователю.
-r job - удаляет указанное задание из очереди. Эта команда также
может быть вызвана с использованием псевдонима: atrm.
-b - выполняет задание, если средняя загрузка системы (посмотреть ее
можно с помощью команды w или top, параметр load average) не превышает
указанное значение (по умолчанию 1.5; как его изменить - смотрите
далее в описании команды atrun). Batch - псевдоним для вызова at с
данным ключом:
# batch -f myjobs +2 minutes
Команда аналогична примеру, приведенному выше для опции -f, но задание
будет выполнено, когда средняя загрузка системы (load average) будет
ниже 1.5. Так, если это условие будет истинно через 2 минуты, то
задание выполнится в указанное время. Иначе оно будет откладываться,
пока загрузка не снизится до требуемого значения.
С остальными параметрами можно ознакомится на страницах справочного
руководства "man at".
Для чего может понадобиться отложенное выполнение команд? Например,
можно поставить на ночь (когда нагрузка меньше, а трафик дешевле)
закачку большого файла:
# echo 'fetch ftp://ftp.ru/pub/bigfile.avi' | at 0200
Здесь мы просто направляем на стандартный ввод программы at строку
'fetch ftp://ftp.ru/pub/bigfile.avi'; данная команда будет запущена в 2:00.
Далее, пусть с 1-го января вступают в силу новые тарифы на услуги,
оказываемые Вашей компанией, и Вы хотите, чтобы информация о них на
Вашем сайте всегда была актуальна (пусть она находится в файле
/usr/local/www/data/tariffs.html). Чтобы не встречать Новый Год
наедине с сервером, создайте файл tariffs.html с новой информацией и
разместите его, скажем, в /home/myhome/temp. Теперь задача обновления
ровно в полночь будет решаться так:
# at midnight Jan 01
cp /home/myhome/temp/tariffs.html /usr/local/www/data/
^D
Можно запустить сборку системы из исходных текстов, когда нагрузка на
систему будет меньше 1.5 (процесс этот ресурсоемкий, но не срочный):
# batch
cd /usr/src
make buildworld > buildword.log // мы же не хотим получить все это по почте?
^D
В предыдущем примере стандартный вывод будет перенаправлен в log-файл,
а сообщения об ошибках поступят на электронный адрес пользователя,
запустившего команду. В общем, полезных примеров можно привести массу.
Еще небольшой совет - вступая во владение новым сервером, проверьте,
какие команды у него в очереди. А то вдруг предыдущий администратор,
уволенный за неумеренное потребление спиртного, оставил Вам "сюрприз"
в виде "rm -fR /*", запланированный на запуск через пару месяцев?
Теперь несколько подробнее рассмотрим механизм, обеспечивающий работу
команды at. Когда с ее помощью формируется отложенное задание, в папке
/var/at/jobs создается файл сценария, содержащий переменные окружения
(какими они были на момент формирования задания; при выполнении
задания эти переменные будут восстановлены) и собственно команды,
которые должны быть выполнены. Периодически (по умолчанию - каждые 5
минут) запускается процесс atrun, который выполняет все задания, срок
выполнения которых истек. Таким образом, говоря выше, что "задание
будет выполнено через 2 минуты", я был не совсем точен. На самом деле
задания выполняются с точностью в 5 минут (как это можно изменить -
читайте ниже). Процесс atrun может быть выполнен с двумя ключами:
# atrun [-l average] [-d]
Ключ -l задает максимальное значение средней загрузки системы, при
котором могут быть выполнены задания, сформированные командой at -b
(batch). Нужно заметить, что если в очереди имеется несколько
batch-заданий, то при снижении загрузки до допустимого уровня будет
запущено только одно (имеющее более раннее время исполнения).
Последующие будут запускаться на выполнение также по одному при каждом
следующем вызове atrun, если средняя загрузка системы все еще будет
позволять сделать это. По умолчанию значение average равно 1.5. Ключ
-d включает режим отладки (все сообщения об ошибках поступают на
стандартный вывод, а не в системные файлы протоколов через механизм
syslog).
После выполнения задания с соответствующего файла сценария снимается
признак исполнимости (x), а при следующем вызове процесса atrun он
удаляется из /var/at/jobs. Просмотреть выполненные, но еще не
удаленные задания с указанием времени, когда они были выполнены,
позволяет команда atq -v (для at с ключом -l ключ -v игнорируется).
Периодический запуск процесса atrun обеспечивается другим механизмом
Unix-систем - cron. О нем и пойдет речь далее.
Демон cron запускается автоматически при старте системы (конкретно -
сценарием /etc/rc.d/cron), и каждую минуту проверяет файлы расписаний
п ользователей и системный файл расписаний /etc/crontab на предмет
наличия заданий, которые должны быть выполнены в данную минуту.
По умолчанию, каждый пользователь может иметь свой файл расписаний.
Изменить это можно с помощью файлов /var/cron/allow и /var/cron/deny
(так же как и для команды at: если файл allow существует, то
использование cron будет разрешено только пользователям, перечисленным
в нем). Файлы расписаний располагаются в /var/cron/tabs с именами,
соответствующими имени пользователя. Для управления ими следует
использовать утилиту crontab, синтаксис которой представлен ниже:
crontab [-u user] (-l | -r | -e)
Опция -u позволяет работать с файлом расписаний указанного
пользователя user, а не текущего, как это происходит без данной опции.
Если Вы работаете через su, лучше всегда использовать этот ключ, чтобы
избежать разночтений. Ключ -l позволяет вывести на экран
пользовательский файл расписаний, -r - удалить его, -e -
редактировать. Для редактирования вызывается редактор, указанный в
переменной окружения EDITOR или VISUAL. Формат строки задания в
пользовательском файле следующий:
Min Hour Day Month WDay Command
То есть через пробельные символы (пробелы и символы табуляции)
указываются минута, час, день, месяц, день недели, когда должна быть
выполнена команда, указанная в шестом поле. Допускаются перечисления
(через запятую: 1,3,5), интервалы (через дефис: 1-5), шаг (после
символа "/": 1-9/2 означает "1,3,5,7,9", то есть каждое второе
значение из указанного диапазона). Звездочка "*" означает все
допустимые значения. Для месяца и дня недели можно использовать их
сокращенные английские названия (первые три буквы), например: Feb,
JUN, tue, Fri (регистр значения не имеет). Диапазоны и перечисления
для имен недопустимы, то есть по имени можно обозначить только один
месяц или день недели. Для числового обозначения дней недели
допустимыми являются числа 0-7, где как 0, так и 7 обозначают
воскресенье. Несколько примеров:
# Запускать программу каждый вторник в 12:00
0 12 * * 2 /usr/home/admin/checkmail
# Выполнять задание через день в январе, марте и с сентября по декабрь
0 0 */2 1,3,9-12 * /usr/local/test/test
# Запускать скрипт 1-го и 15-го числа каждого месяца в 2:05,
# а также по воскресеньям (Day и WDay работают в режиме "ИЛИ")
5 2 1,15 * Sun /home/script
Демон cron при каждой активизации проверяет дату изменения файла
/etc/crontab и директории /var/cron/tabs. Если они изменились, то он
перечитывает все изменения и учитывает их при последующих вызовах.
Поскольку утилита crontab после редактирования файла заданий меняет
дату изменения для папки /var/cron/tabs, то после того, как в
пользовательский файл расписаний будут внесены изменения, нет нужды в
перезапуске процесса cron - эти изменения будут учтены при следующей
активизации. Именно по этой причине для редактирования
пользовательских файлов расписаний следует использовать команду
crontab -e, а не редактировать файлы непосредственно. Для файла
/etc/crontab дата изменения проверяется отдельно, поэтому его можно
изменять обычным редактором (естественно, для этого нужны права root).
Формат файла crontab также допускает вместо первых пяти позиций,
означающих время выполнения задания, использовать предопределенные
значения:
* @reboot (выполнять при загрузке операционной системы)
* @yearly (выполнять ежегодно в полночь 1-го января)
* @monthly (выполнять ежемесячно в полночь 1-го числа)
* @weekly (выполнять в полночь каждый понедельник)
* @daily (выполнять ежедневно в 0:00)
* @hourly (выполнять в начале каждого часа).
Например:
# Выполнять 1-го числа каждого месяца в 0:00
@monthly /usr/local/billing/close_month.pl
Помимо собственно заданий, файл crontab может содержать строки,
задающие системные переменные, с которыми задания будут
отрабатываться. Так, можно задать оболочку, в которой будут
исполняться сценарии (переменная SHELL, по умолчанию это sh),
переменную PATH. Если нужно, чтобы выводимая выполняемыми командами
информация пересылалась на почтовый ящик не владельца файла расписаний
(как это происходит по умолчанию), а на другого пользователя, для
этого можно использовать переменную MAILTO. Пустое значение
(MAILTO="") приведет к тому, что сообщения будут перенаправляться в
/dev/null.
Формат системного файла /etc/crontab несколько отличается от
пользовательского: на шестой позиции указывается имя пользователя, с
правами которого должна запускаться команда (и опционально - группа,
отделенная от имени пользователя двоеточием, например "root:wheel"), а
сама команда отодвигается на седьмую. Например, вызов atrun задан в
этом файле такой строкой:
*/5 * * * * root /usr/libexec/atrun
Из этой записи видно, что процесс atrun, обеспечивающий отработку
заданий, сформированных командой at, будет запускаться каждые 5 минут.
При необходимости повысить точность отработки заданий по at, следует
соответствующим образом изменить приведенную выше строку.
С использованием механизма cron выполняется и такая важная задача, как
ротация log-файлов (см. строку, отвечающую за запуск newsyslog). По
умолчанию процесс newsyslog запускается каждый час и проводит ротацию
(архивирование и перезапись) log-файлов в соответствии с настройками в
/etc/newsyslog.conf. На загруженных системах с подробным
протоколированием ротация может понадобиться чаще, чем раз в час, для
чего следует изменить соответствующую строку в /etc/crontab.
С механизмом cron связана еще одна полезная вещь - автоматическое
обслуживание системы, обеспечиваемое с помощью periodic-сценариев. В
системном crontab можно заметить следующие три строки:
Утилита periodic исполняет все сценарии, расположенные в папке,
соответствующей параметру, переданному ей при вызове. Эта папка ищется
в каталоге /etc/periodic. Например, запуск periodic daily приведет к
выполнению всех скриптов, расположенных в /etc/periodic/daily. Также
существует возможность создавать свои периоды обслуживания, для чего
должна быть создана новая директория в /etc/periodic, в которую
помещаются необходимые скрипты, и добавлена соответствующая строка
запуска в crontab. Настроить обслуживание системы под собственные
нужды можно в файле /etc/periodic.conf (по умолчанию отсутствует).
Настройки по умолчанию заданы в /etc/defaults/periodic.conf. Как
видно, эти сценарии выполняют очистку временных файлов, резервное
копирование жизненно важной информации, формируют отчеты о состоянии
системы и т.д. Основная работа приходится на ежедневное обслуживание
(daily). Можно заметить, что в папке /etc/periodic присутствует
поддиректория security, хотя в crontab вызова для нее нет. Если
поискать внимательно, то команду вызова "periodic security" можно
найти в файле /etc/periodic/daily/450.status.security, то есть сбор
сведений о состоянии безопасности выполняется в ходе ежедневного
обслуживания, но для удобства вынесен в отдельную поддиректорию, и по
этой же причине отчет высылается отдельным письмом.
Поскольку порядок исполнения сценариев определяется в результате
сортировки их имен, для формирования нужной последовательности
исполнения служит трехзначное число, предваряющее имя почти каждого
файла.
В директориях daily, weekly и monthly можно заметить сценарии
999.local. Они позволяют запускать так называемые локальные
periodic-сценарии, которые перечисляются в файлах /etc/daily.local,
/etc/weekly.local и /etc/monthly.local соответственно. Использование
локальных сценариев для собственных нужд более предпочтительно, чем
добавление скриптов в /etc/periodic, поскольку позволяет четко
разделить системные и п ользовательские сценарии и тем самым упростить
сопровождение и обновление системы.
Как и для остальных команд, выполняемых по cron, весь стандартный
вывод будет перенаправлен на электронный адрес пользователя-владельца
таблицы crontab или указанного в переменной MAILTO.
Придумывать полезные примеры для cron труда не составляет. Приведу два
первых, пришедших в голову. Предположим, нам нужно контролировать
состояние сети. Для этого достаточно занести в Ваш пользовательский
файл расписаний (системный файл лучше использовать исключительно по
системному назначению) строку:
Как это будет работать? В начале каждого часа будет выполняться
команда ping, посылающая десять пакетов на адрес Вашего провайдера. Из
возвращаемой информации будет выбрана последняя строка с результатами,
которая будет записана в файл ping.log для последующего анализа таких
величин, как среднее время передачи пакета, величина девиации и т.д.
Причем в файл будут заноситься только результаты успешного пинга, а
все ошибки (типа "Host is down") будут отправляться по электронной
почте владельцу файла расписаний.
Следующая строка позволит Вам автоматически корректировать Ваши
(точнее, системные) часы с использованием сервера точного времени:
12 4 * * * /usr/sbin/ntpdate ntp.alaska.edu
Естественно, Вы можете использовать для получения информации о точном
времени тот сервер, который Вам больше нравится (список серверов можно
получить если на любом поисковике подать запрос "Public ntp server").
Еще несколько слов о путях к вызываемым программам. Для сервиса at
переменная PATH и каталог, в котором задание будет выполняться,
соответствуют тем значениям, которые были на момент формирования
задания. То есть если я запускаю at, находясь в директории /etc, то и
задание будет выполняться так, как будто оно было запущено из этой же
директории, и соответственно, все относительные пути, имеющиеся в
запускаемом сценарии, будут разрешаться относительно этого каталога.
Cron ведет себя несколько иначе - он запускает задания из домашнего
каталога пользователя, а в качестве переменной PATH использует то
значение, которое задано в файле расписаний (см. crontab -l) в
переменной PATH. По умолчанию PATH=/usr/bin:/bin. Проверить это
достаточно просто - сформируйте задание с командами pwd и echo $PATH,
и Вы получите письмо со значениями этих параметров на момент
выполнения задания.
На этом знакомство со службами выполнения заданий по расписанию можно
завершить. Как обычно, дополнительную информацию можно получить на
страницах справочного руководства man: at(1), atrun(8), cron(8),
crontab(1), crontab(5), periodic(8), periodic.conf(5). Также мощнейшим
средством освоения системы по-прежнему остается "научный тык".