Выполнение только одного экземпляра сценария Perl кроном

Я должен выполнить сценарий Perl кроном периодически (~every 3-5 минут). Я хочу удостовериться, что только один экземпляр сценария Perl будет работать во время, поэтому следующий цикл не запустится, пока предыдущий не закончен. Это могло быть достигнуто некоторой встроенной функциональностью крона, Perl, или я должен обработать его на уровне сценария?

Я довольно плохо знаком с Perl и кроном, таким образом, справка и общие рекомендации ценятся.

20
задан Gennady Shumakher 9 February 2010 в 22:03
поделиться

5 ответов

Мне всегда везло с использованием File::NFSLock для получения эксклюзивной блокировки самого скрипта.

use Fcntl qw(LOCK_EX LOCK_NB);
use File::NFSLock;

# Try to get an exclusive lock on myself.
my $lock = File::NFSLock->new($0, LOCK_EX|LOCK_NB);
die "$0 is already running!\n" unless $lock;

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

14
ответ дан 29 November 2019 в 23:48
поделиться

Используйте File::Pid для хранения pid скрипта в файле, который скрипт должен проверять при запуске и прерывать, если он найден. Вы можете удалить pid-файл после завершения работы скрипта, но это не обязательно, так как позже вы можете просто проверить, жив ли еще этот идентификатор процесса (это также учтет случаи, когда ваш скрипт неожиданно прерывается):

use strict;
use warnings;
use File::Pid;

my $pidfile = File::Pid->new({file => /var/run/myscript});
exit if $pidfile->running();

$pidfile->write();

# ... rest of script...

# end of script
$pidfile->remove();
exit;
11
ответ дан 29 November 2019 в 23:48
поделиться

Обычно каждый процесс открывает и блокирует определенный файл. Затем процесс считывает идентификатор процесса, содержащийся в файле.

Если процесс с этим идентификатором запущен, опоздавший завершает работу. В противном случае новый победитель записывает свой идентификатор процесса ( $$ в Perl) в pid-файл, закрывает дескриптор (который снимает блокировку) и приступает к своим делам.

Пример реализации ниже:

#! /usr/bin/perl

use warnings;
use strict;

use Fcntl qw/ :DEFAULT :flock :seek /;

my $PIDFILE = "/tmp/my-program.pid";
sub take_lock {
  sysopen my $fh, $PIDFILE, O_RDWR | O_CREAT or die "$0: open $PIDFILE: $!";
  flock $fh => LOCK_EX                       or die "$0: flock $PIDFILE: $!";

  my $pid = <$fh>;
  if (defined $pid) {
    chomp $pid;
    if (kill 0 => $pid) {
      close $fh;
      exit 1;
    }
  }
  else {
    die "$0: readline $PIDFILE: $!" if $!;
  }

  sysseek  $fh, 0, SEEK_SET or die "$0: sysseek $PIDFILE: $!";
  truncate $fh, 0           or die "$0: truncate $PIDFILE: $!";
  print    $fh "$$\n"       or die "$0: print $PIDFILE: $!";
  close    $fh              or die "$0: close: $!";
}

take_lock;
print "$0: [$$] running...\n";
sleep 2;
3
ответ дан 29 November 2019 в 23:48
поделиться

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

1
ответ дан 29 November 2019 в 23:48
поделиться

Учитывая частоту, я бы обычно писал демона (сервера), который спокойно ждет между запусками задания (например, sleep () ), вместо того, чтобы пытаться использовать cron для достаточно детального доступа.

При необходимости в системах Unix / Linux вы можете запустить его из / etc / inittab (или замены), чтобы убедиться, что он всегда работает и автоматически перезапускается, когда процесс завершается или умирает.

Добавлено : (и некоторые несущественные вещи удалены)

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

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

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

Подход с использованием модуля Fcntl и Perl sysopen с флагом O_EXCL (или O_RDWR | O_CREAT | O_EXCL ) был задан Грег Бэкон . Единственное отличие, которое я хотел бы сделать, это объединить эксклюзивную блокировку с вызовом sysopen (т.е. использовать предложенные мной флаги) и удалить тогда избыточный вызов flock . Да, и я бы следовал файловой системе UNIX (и Linux FHS) и соглашениям об именах /var/run/daemonname.pid .

Другой подход - использовать djb daemontools или аналогичный , чтобы «демонизировать» задачу.

-1
ответ дан 29 November 2019 в 23:48
поделиться
Другие вопросы по тегам:

Похожие вопросы: