我有一个用Perl编写的守护进程,它使用Inotify2来监视传入文件的目录。在每个文件到达时,守护进程将分叉一个子进程。现在,似乎有太多文件同时到达(因此太多的分叉),因为我的日志文件中出现了这个错误:
Cannot allocate memory at notifyd.pl line ...
这是fork()的结果。
基本上我有以下代码:
my $inotify = new Linux::Inotify2() or die($!);
foreach my $k (@PATHS) {
$inotify->watch($k,
IN_MOVE_SELF|IN_DELETE_SELF|IN_CLOSE_WRITE, \&watcher) or die($!);
}
$inotify->blocking(1) or die($!);
for(;;) {
$inotify->poll() or die($!);
}
使用watcher函数执行fork然后执行execv:
sub watcher {
my $e = shift;
my $pid = fork();
if(!defined $pid) {
print "[ERROR]", $!;
}
elsif($pid == 0) {
my @args = ($e->fullname, $e->mask);
exec($childprocess, @args) or die($!);
}
}
我不能错过任何过程。
有没有人建议我如何改进这一点并确保fork不会失败?
编辑:由于守护程序不会响应SIGCHLD,因此子进程一旦退出就会变成僵尸。因此很多僵尸子进程可能是fork()失败的原因。在分叉之前守护进程现在$SIG{CHLD} = 'IGNORE';
。
答案 0 :(得分:2)
通过添加另一层间接来解决问题。
收到活动后,请将文件名放入job queue。当资源合理免费时,队列开始处理文件的新作业;这个方案保证事件最终会被采取行动,而不是立即采取行动。
答案 1 :(得分:2)
使用更强大的后台流程管理器,例如Forks::Super
例如,此设置一次最多可运行10个货叉。当所有10个叉子都忙时进入的新请求将被放入队列中。队列中的作业将在其他后台进程完成且资源可用时运行。
use Forks::Super MAX_PROC => 10, ON_BUSY => 'queue';
...
sub watcher {
my $e = shift;
fork {
sub => sub {
my @args = ($e->fullname, $e->mask);
exec($childprocess, @args) or die($!);
}
};
}