如何在初始化脚本中获取 perl 守护进程的 PID?

How to get PID of perl daemon in init script?

我有以下 perl 脚本:

#!/usr/bin/perl

use strict;
use warnings;
use Proc::Daemon;

Proc::Daemon::Init;

my $continue = 1;
$SIG{TERM} = sub { $continue = 0 };

while ($continue) {
        # stuff
}

我的初始化脚本中有以下内容:

DAEMON='/path/to/perl/script.pl'
start() {
    PID=`$DAEMON > /dev/null 2>&1 & echo $!`
    echo $PID > /var/run/mem-monitor.pid
}

问题是,这个returns错误的PID!这 returns 守护进程为 运行 时启动的父进程的 PID,但该进程立即被终止。我需要获取子进程的PID!

Proc::Daemon

Proc::Daemon does the following:
...
9. The first child transfers the PID of the second child (daemon) to the parent. Additionally the PID of the daemon process can be written into a file if 'pid_file' is defined. Then the first child exits.

然后在 new ( %ARGS )

pid_file
Defines the path to a file (owned by the parent user) where the PID of the daemon process will be stored. Defaults to undef (= write no file).

另请参阅 Init() 方法说明。这一切都意味着您可能想先使用 new

关键是 grand-child 进程是守护进程。然而,childr 传递 pid 并且它可供父级使用。如果在构造函数(守护进程的)中设置了 pid_file => $file_name,则将 pid 写入该文件。


一条评论要求不要让 shell 脚本依赖于另一个脚本编写的文件。

我可以看到两种方法。

  • 打印由 $daemon->Init() 从父级返回的 pid,并在 shell 中获取它。这被问题中的重定向打败了,但我不知道为什么需要它们。父子关系在所有设置完成后立即退出,而守护进程与一切分离。

  • Shell 脚本可以使用所需的 log-file 名称作为参数启动 Perl 脚本,让它通过上述过程将守护程序 pid 写入该文件。该文件仍然由 Perl 输出,但重要的是它由 shell 脚本决定。

我想在下面的评论中包含一个声明。我认为这些优于想到的其他两件事:从 shell 保存的 config-style 文件中选取文件名更复杂,而解析过程 table 可能不可靠。

感谢 zdim 和 Hakon 的建议。它们当然是可行的,并让我走上了正确的轨道,但最终我走了一条不同的路。而不是依赖$!,我使用psawk来获取PID,如下:

DAEMON='/path/to/perl/script.pl'

start() {
    $DAEMON > /dev/null 2>&1
    PID=`ps aux | grep -v 'grep' | grep "$DAEMON" | awk '{print }'`
    echo $PID > /var/run/mem-monitor.pid
}

这有效并满足了我的强迫症!请注意 grep "$DAEMON".

中“$DAEMON”周围的双引号

我以前见过这个,不得不求助于使用 STDERR 将子 PID 发送回调用 shell 脚本。我一直认为这是由于提到的退出代码的不可靠性 - 但文档中的细节并不明确。请尝试这样的事情:

#!/usr/bin/perl
use strict;
use warnings;
use Proc::Daemon;

if( my $pid = Proc::Daemon::Init() ) {
    print STDERR $pid;
    exit;
}

my $continue = 1;
$SIG{TERM} = sub { $continue = 0 };

while ($continue) {
    sleep(20);
    exit;
}

使用这样的调用脚本:

#!/bin/bash
DAEMON='./script.pl'
start() {
    PID=$($DAEMON 2>&1 >/dev/null)
    echo $PID > ./mem-monitor.pid
}
start;

当bash 脚本为运行 时,它将捕获STDERR 输出(包含正确的PID),并将其存储在文件中。 Perl 脚本生成的任何 STDOUT 都将被发送到 /dev/null - 尽管这不太可能,因为第一级 Perl 脚本确实(在这种情况下)很早就退出了。