Скажем, у вас есть простое resource.cgi
:
#! /usr/bin/perl
use warnings;
use strict;
use Reader;
use CGI qw/ :standard /;
print header("text/plain"),
"Contents:\n",
Reader::data,
"-" x 40, "\n";
Его выход
Content-Type: text/plain; charset=ISO-8859-1
Contents:
This is a data file
with some very interesting
bits.
----------------------------------------
Самое интересное в Reader.pm
, которое начинается с привычного шаблона:
package Reader;
use warnings;
use strict;
use Fcntl qw/ :DEFAULT :flock :seek /;
use POSIX qw/ setsid /;
Далее он определил точки встречи:
my $PIDFILE = "/tmp/reader.pid";
my $DATA = "/tmp/file.dat";
my $PIPE = "/tmp/reader.pipe";
Под import
вызывается как часть use Module
. Если демон уже запущен, тогда делать нечего. В противном случае мы отключаем демон и записываем его идентификатор процесса в $PIDFILE
.
sub import {
return unless my $fh = take_lock();
my $child = fork;
die "$0: fork: $!" unless defined $child;
if ($child) {
print $fh "$child\n" or die "$0: write $PIDFILE: $!";
close $fh or die "$0: close $PIDFILE: $!";
return;
}
# daemonize
close $fh;
chdir "/";
open STDIN, "<", "/dev/null";
open STDOUT, ">", "/dev/null";
open STDERR, ">", "/dev/null";
setsid;
open $fh, "<", $DATA or die;
undef $/;
my $data = <$fh>;
close $fh;
while (1) {
open my $fh, ">", $PIPE or die;
print $fh $data or die;
close $fh;
}
}
Каждый клиент должен ждать своей очереди, чтобы получить блокировку на $PIDFILE
. Получив блокировку, мы проверяем, что указанный процесс все еще работает, и при необходимости создаем именованный канал.
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;
return;
}
}
else {
die "$0: readline $PIDFILE: $!" if $!;
}
sysseek $fh, 0, SEEK_SET or die "$0: sysseek $PIDFILE: $!";
truncate $fh, 0 or die "$0: truncate $PIDFILE: $!";
unless (-p $PIPE) {
system("mknod", $PIPE, "p") == 0
or die "$0: mknod exited " . ($? >> 8);
}
$fh;
}
Наконец, чтение канала тривиально:
sub data {
open my $fh, "<", $DATA or die "$0: open $DATA: $!";
local $/;
scalar <$fh>;
}
Не забудьте вернуть истинное значение из модуля:
1;
Вы заметите, что операции в демоне все еще могут быть неудачными. Для вашего здравого смысла, вам нужно как-то регистрировать события, а не задыхаться.
Относительно того, будут ли хосты разрешать долго выполняющиеся процессы, это будет зависеть от поставщика к поставщику, но даже если ваш демон время от времени отключается, приведенный выше код перезапустит его по требованию.