【问题标题】:How to get PID of perl daemon in init script?如何在 init 脚本中获取 perl 守护进程的 PID?
【发布时间】:2017-02-17 07:52:05
【问题描述】:

我有以下 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
}

问题是,这返回了错误的 PID!这将返回在守护进程运行时启动的父进程的 PID,但该进程会立即被终止。我需要获取子进程的PID!

【问题讨论】:

  • 参见Proc::Daemon 的文档。有一个名为pid_file 的选项。 PID 将被写入此文件。
  • 是的,我知道我可以在 Perl 中使用这个选项。但是,我想在 init 脚本中保留这样的功能(创建、删除 PID 文件),而不是在 perl 中完成 1/2,在 init 脚本中完成 1/2。
  • 您可以尝试将文件名从 shell 脚本传递到命令行上的 Perl 脚本,例如$DAEMON $pid_file > /dev/null 2>&1 .... 然后 Perl 脚本可以使用这个文件名 $pid_file 来创建 pid 文件

标签: linux perl daemon


【解决方案1】:

Proc::Daemon

Proc::Daemon 执行以下操作:
...
9. 第一个孩子将第二个孩子(守护进程)的 PI​​D 传递给父母。此外,如果定义了“pid_file”,则可以将守护进程的 PID 写入文件。然后第一个孩子退出。

然后在new ( %ARGS )

pid_file
定义将存储守护进程PID 的文件(由父用户拥有)的路径。默认为 undef(= 不写入文件)。

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

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


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

我可以看到两种方法。

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

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

我想在下面的评论中加入一份声明。我认为这些优于我想到的另外两件事:从 shell 保存的配置样式文件中选择文件名更复杂,而解析进程表可能不可靠。

【讨论】:

  • 感谢您的解释。有什么方法可以从 shell 访问孙子的 PID 吗?正如我在 cmets 中对原始问题所指出的那样,我宁愿避免在 Perl 脚本中创建 PID 文件。这对我来说并不公平,因为 init 脚本必须依赖于 Perl 脚本创建的文件。
  • @Bintz 我认为这是合理的。我在帖子中添加了另外两种可以查看的方式。我能想到的另外两个看起来不太好——(1)Perl 脚本从某种配置文件中提取要使用的文件名,由 shell 保存。这会比将文件名传递给脚本更复杂,也不会更好,如答案 (2) Shell 通过跟踪和分析进程表来找到 pid。那将是非常混乱和不可靠的。 // 我还没有看到其他方法。
【解决方案2】:

我以前见过这种情况,不得不求助于使用 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 脚本确实(在这种情况下)很早就退出了。

【讨论】:

    【解决方案3】:

    感谢 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 $2}'`
        echo $PID > /var/run/mem-monitor.pid
    }
    

    这可以满足我的强迫症!请注意 grep "$DAEMON" 中 "$DAEMON" 周围的双引号。

    【讨论】:

      猜你喜欢
      • 2014-12-11
      • 2014-10-10
      • 1970-01-01
      • 1970-01-01
      • 2011-02-27
      • 2016-05-08
      • 1970-01-01
      • 2013-06-02
      • 2015-07-05
      相关资源
      最近更新 更多