【发布时间】:2011-01-15 00:39:54
【问题描述】:
我需要定期(约每 3-5 分钟)通过 cron 运行 Perl 脚本。我想确保一次只运行一个 Perl 脚本实例,因此下一个循环在前一个循环完成之前不会开始。可以/应该通过 cron、Perl 的某些内置功能来实现,还是我需要在脚本级别处理它?
我对 Perl 和 cron 还很陌生,因此非常感谢您的帮助和一般性建议。
【问题讨论】:
-
你说的是哪个平台? *尼克斯?
我需要定期(约每 3-5 分钟)通过 cron 运行 Perl 脚本。我想确保一次只运行一个 Perl 脚本实例,因此下一个循环在前一个循环完成之前不会开始。可以/应该通过 cron、Perl 的某些内置功能来实现,还是我需要在脚本级别处理它?
我对 Perl 和 cron 还很陌生,因此非常感谢您的帮助和一般性建议。
【问题讨论】:
我一直很幸运使用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;
这与其他锁文件建议有点相同,除了尝试获取锁之外我不需要做任何事情。
【讨论】:
exit 0 unless $lock;
【讨论】:
使用File::Pid 将脚本的 pid 存储在文件中,脚本应在开始时检查该文件,如果找到则中止。您可以在脚本完成后删除 pidfile,但这并不是真正必要的,因为您可以稍后检查该进程 id 是否仍然存在(这也将解释脚本意外中止的情况):
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;
【讨论】:
典型的做法是让每个进程打开和锁定某个文件。然后进程读取文件中包含的进程ID。
如果具有该 ID 的进程正在运行,则后来者安静地退出。否则,新的获胜者将其进程 ID(Perl 中的$$)写入 pidfile,关闭句柄(释放锁),然后继续其业务。
下面的示例实现:
#! /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;
【讨论】:
我一直使用这个 - 小而简单 - 不依赖任何模块并且适用于 Windows + Linux。
use Fcntl ':flock';
### Check to make sure there is only one instance ###
open SELF, "< $0" or die("Cannot run two instances of this program");
unless ( flock SELF, LOCK_EX | LOCK_NB ) {
print "You cannot run two instances of this program , a process is still running";
exit 1;
}
【讨论】:
AFAIK perl 没有内置这样的东西。您可以在启动应用程序时轻松创建一个临时文件,并在脚本完成后将其删除。
【讨论】:
考虑到我通常会编写一个守护进程(服务器)的频率,它会在作业运行之间很好地等待(即sleep()),而不是尝试使用 cron 进行相当细粒度的访问。
如有必要,在 Unix / Linux 系统上,您可以从 /etc/inittab(或替换)运行它,以确保它始终运行,并在进程被杀死或终止时自动重新启动。
添加:(并删除了一些不相关的内容)
始终存在(运行,但大多是空闲的)守护程序方法的好处是消除了脚本的并发实例被 cron 自动启动的可能性。
但这确实意味着您有责任正确管理时间,例如在存在重叠的情况下(即之前的运行仍在运行,而新的触发器发生)。这可以帮助您决定是使用分叉守护程序还是非分叉设计。线程在这种情况下没有任何优势,因此无需考虑它们的使用。
这并不能完全消除多个进程运行的可能性,而是许多守护进程的常见问题。典型的解决方案是使用信号量,例如文件上的互斥锁,以防止运行第二个实例。进程结束时文件锁会自动忘记,因此在异常终止(例如电源故障)的情况下,锁本身不需要清理。
使用 Fcntl 模块并使用带有 O_EXCL 标志(或 O_RDWR | O_CREAT | O_EXCL)的 Perl sysopen 的方法是 given by Greg Bacon。我要做的唯一区别是将排他锁定结合到 sysopen 调用中(即使用我建议的标志),然后删除多余的 flock 调用。哦,我会遵循 UNIX(和 Linux FHS)文件系统和 /var/run/daemonname.pid 的命名约定。
另一种方法是使用 djb 的 daemontools 或 similar 来“守护”任务。
【讨论】: