我有一个perl模块,我的收集脚本库使用。这些脚本用于扫描我的网络,在我的网络设备上执行各种任务等。
大概有15 users
,我一次只想要1
个人来运行收集脚本。如果第二个用户试图运行脚本,那么我希望他们等到第一个人完成。
下面的代码只是一个试验台,因此我可以在投入生产之前使其正常工作。我有一个具有nap
功能的模块。我一次只想要一个人打盹。
sub nap {
my $program = shift;
my @arr;
#open file to check the queue
open(IN, $path); @arr=<IN>; close IN;
#if someone is in the queue, print it out!
$|++;
if (@arr > 0) { print @arr; }
#keep checking the queue, once the queue is empty it's my turn!
while (@arr != 0) {
open(IN, $path); @arr=<IN>; close IN;
sleep 1;
}
#it's my turn, put my name in the queue
open(IN,">",$path);
print IN "$ENV{USER},$program";
close IN;
#take a nap!
print "\n Sleep starting \n";
sleep 10;
#I'm finished with my nap, clear the queue so other's can go
open(IN,">",$path);
close IN;
print "\nsleep over\n";
}
如果1 user
正在等待,我的问题是有效的,但是如果2 users
正在等待,他们仍然会在同一时间小睡(在第一个用户完成之后)
可以锁定或阻止此文件吗?我已经看过flock
,但无论你如何锁定它,用户仍然可以阅读。
这是一个正确的解决方案吗?还是有更好的东西用于这些目的?
答案 0 :(得分:4)
您的解决方案不正确。首先,你使用普通的open
来缓冲读写操作,当你希望多个进程通过一个文件进行通信时,会导致复杂化。
正如您似乎已经怀疑的那样,并且正如其他人所评论的那样,在Unix类操作系统上没有(合理的)方法来强制它,以便只有一个进程可以从文件中读取。在某种意义上,处理它的正确方法是使用锁定文件,并且只有当前持有锁的进程从数据/通信文件中读取。请查看perldoc -f flock
了解相关详情。
不幸的是,Unix上的文件锁确实有一些缺点。特别是如果锁定文件驻留在网络文件系统上,则它们可能不可靠。例如,使用NFS,功能锁取决于安装运行锁守护程序的文件系统的所有计算机。解决这个问题的一种有点愚蠢但传统的方法是滥用mkdir
的语义。如果一堆进程都试图创建一个具有相同名称的目录,那么可以保证只有其中一个会成功(好吧,或者没有,但现在让我们跳过它)。您可以使用它来同步进程。在进程开始执行只能一次执行的操作之前,它会尝试创建具有预定名称的目录。如果成功,那很好,它可以继续下去。如果它失败了,其他人已经在工作,它必须等待。当活动进程完成工作时,它将删除目录,以便另一个进程可以成功创建它。
无论如何,基本信息是你需要两个文件系统的东西:一个是你的进程用来确定哪一个可以工作,一个是实际工作。
答案 1 :(得分:1)
您可以锁定文件的DATA部分以锁定文件本身,因此您可以(ab)使用它来控制对该脚本的独占访问。
我把它放在库文件s
中:
nap.pl
然后我打开了3个终端并在每个终端中运行:
#!usr/bin/env perl
use strict;
use Fcntl qw(LOCK_EX LOCK_NB);
sub nap {
## make sure this script only runs one copy of itself
until ( flock DATA, LOCK_EX | LOCK_NB) {
print "someone else has a lock\n";
sleep 5;
}
}
__DATA__
This exists to allow the locking code at the beginning of the file to work.
DO NOT REMOVE THESE LINES!
第一个终端立即打印了我/ tmp目录的内容。 第二个终端打印“别人有锁”,然后在5秒后打印出/ tmp的内容。 第三个终端打印“别人有一个锁”两次,一次立即然后一次在5秒后,然后打印/ tmp /.
的内容请注意将它放在库中的位置,但是你需要确保没有锁定不相关的子程序。
我个人会将锁码放在每个收集脚本中,而不是放在库中。收集脚本是你实际上只想运行一个实例的。看起来你的标题不准确:你不是试图独自读取文件,而是试图独占运行文件。