![从 /etc/inittab 重新启动 Perl 脚本(套接字守护进程)](https://linux22.com/image/551226/%E4%BB%8E%20%2Fetc%2Finittab%20%E9%87%8D%E6%96%B0%E5%90%AF%E5%8A%A8%20Perl%20%E8%84%9A%E6%9C%AC%EF%BC%88%E5%A5%97%E6%8E%A5%E5%AD%97%E5%AE%88%E6%8A%A4%E8%BF%9B%E7%A8%8B%EF%BC%89.png)
我跑一款小型多人纸牌游戏高峰时段大约有 500 名用户:
客户端是Flash,服务器是Perl。
Perl 服务器绑定到端口 8080,即只能启动它的 1 个实例(重要细节)。
Perl 服务器轮询 () 的 TCP 套接字并仅分叉一次 - 在启动时通过调用此方法:
sub daemonize {
die "Can not fork: $!\n" unless defined (my $child = fork());
# the parent should die
exit 0 if $child;
setsid();
open(STDIN, '</dev/null');
open(STDOUT, '>/tmp/pref.txt');
open(STDERR, '>&STDOUT');
chdir('/');
umask(0);
}
....
$tcpSocket = IO::Socket::INET->new(Proto => 'tcp',
LocalPort => 8080,
Listen => SOMAXCONN,
ReuseAddr => 1,
);
die "Can not create listening TCP socket: $!\n"
unless defined $tcpSocket;
它在 CentOS 5.6 Linux/64 位、PostgreSQL 8.4.8 和 Perl 5.8.8 上运行。
因为我的预算很少,而且我已经遇到了足够多的麻烦,所以我想尽可能少地使用附加软件 - 这样我就可以快速更换主机或重新安装我的廉价服务器。这就是为什么我只记录到 /tmp/pref.txt 而不是安装 syslog-ng。这就是为什么我想使用 /etc/inittab 重新启动我的 Perl 守护进程。
我的 Perl 守护进程运行基本稳定,但大约每周都会崩溃一次,
May 29 11:06:46 myhost kernel: pref.pl[3113]: segfault at 00007fffa21e6fd8 rip 0000003cce274460 rsp 00007fffa21e6fd0 error 6
由于我厌倦了手动重启服务器,因此我尝试将其添加到 /etc/inittab:
pref:3:respawn:/bin/su -c '/usr/local/pref/pref.pl' nobody
(并且我已经将夜间 cronjob 添加到“pkill pref.pl”,希望通过这种方式刷新 perl)。
不幸的是,这不能按预期工作 - 在 /var/log/messages 中我看到脚本被一次又一次地启动:
Jun 2 18:55:56 myhost init: Id "pref" respawning too fast: disabled for 5 minutes
Jun 2 19:00:58 myhost init: Id "pref" respawning too fast: disabled for 5 minutes
Jun 2 19:06:02 myhost init: Id "pref" respawning too fast: disabled for 5 minutes
我在这里做错了什么?我希望能够在这里使用 /etc/inittab,因为我记得几年前在工作中遇到过类似的情况(也是用 Perl 守护进程),当时它运行良好……
谢谢!Alex
更新:
我的游戏没有崩溃,perl 解释器崩溃了(但不经常,大约一周一次)。
我的问题是如何运行 Perl 守护进程(即,一开始分叉一次然后绑定到 TCP 端口的 Perl 脚本)来自 /etc/inittab?
答案1
只需从代码中删除 fork()。到目前为止,您已经编写了一段代码,希望将其从控制台分离并在后台运行。但是,当进程从 inittab 运行时,它们需要保持连接。如果您删除 fork(),它将保持连接到 init,并且 init 将能够正确监视/启动/停止/重启
答案2
我无法谈论您的 inittab 解决方案,希望其他人可以,但我确实想建议您将“File::Pid”视为要添加到脚本中的内容 - 在守护进程调用之后(这很重要 - 您的进程 ID 在 fork 之后会发生变化)。
use File::Pid;
my $pidfile = File::Pid->new({
file => '/var/run/myprogram.pid',
pid => $$,
});
if ( my $num = $pidfile->running ) {
exit;
}
$pidfile->write;
这给你带来了一些好处 - 一是它可以确保你不会同时运行它的两个副本(你说这很重要),二是它还可以让你在需要时安全地调用此程序,提供简单的 keepalive。只需每 10 分钟从 cron 调用一次你的代码,如果它正在运行,它什么也不做,如果它关闭,它会重新启动。