【问题标题】:Instantly stopping a perl daemon立即停止 perl 守护进程
【发布时间】:2014-06-01 09:36:42
【问题描述】:

我在 Ubuntu 上运行了一个用 Perl 编写的守护进程,它现在是单线程的。当它启动时,它会执行通常的 Proc::Daemon 操作,然后进入一个由布尔值限制的 while 循环。守护程序使用“服务守护程序启动”正常启动。但是当我想用“service daemon stop”杀死它时,它并没有停止。

应该通过使用信号处理程序翻转布尔值来实现停止:

$SIG{INT} = $SIG{TERM} = $SIG{HUP} = \&signalHandler;

sub signalHandler {
    $continue = 0;
}

不幸的是,主循环也有一个睡眠。所以守护进程在睡眠结束之前不会结束。我希望守护进程立即结束。但我不想设置非常低的睡眠时间。事实上,我希望能够设置几个小时的睡眠时间,如果这是它决定应该做的。

最好的方法是什么?

我正在考虑在主循环中调用某种阻塞函数而不是睡眠。在某个线程死亡之前,此阻塞函数不会返回。然后在信号处理程序中,我会简单地杀死那个睡眠线程。这是一个好方法吗?我该怎么做呢,我以前从未在 Perl 中做过多线程。

感谢您的帮助

【问题讨论】:

  • 如果您发送SIGALRM,睡眠呼叫会发生什么?
  • 它停止了..不知道..

标签: linux multithreading perl daemon


【解决方案1】:

以下是针对这种情况的解决方法:

1. 当您的软件开始运行时创建一个命名管道,例如mkfifo

2.sleep 调用替换为select 系统调用(作为替代方案,您可以使用perl 包IO::Select 而不是select 系统调用)。

设置选择以监视管道何时准备好输入。

select 允许指定超时,就像睡眠一样。

当超时或满足文件条件时,select 调用返回。

3. 如果发生超时,请运行循环的主体代码。如果管道上出现数据,则终止。

4. 要终止,请将数据发送到命名管道。

【讨论】:

  • 我不会称其为解决方法,如果您有任何要监控的 IO,在任何情况下睡眠都是更好的解决方案。
【解决方案2】:

如果您还没有这样做,您可以将任何与关闭相关的清理移动到它自己的子程序中,然后让signalHandler() 调用该清理子程序,然后简单地调用exit。 然后编辑您的启动/停止脚本以发送 SIGALRM。 SIGALRM 将中断睡眠。如果您的脚本被编辑以捕捉到这一点,那么一切都会立即停止。

延伸阅读:sleep()

【讨论】:

  • 如果我采用这种方法,我会颠倒$sleeptime$sleepsegment 的交互,这样$sleepsegment 是每个段的持续时间而不是段数。由于 OP 关心响应能力,因此最好“根据需要执行尽可能多的 5 秒 sleeps”而不是“将 sleep 分成 5 个部分”,因为 OP 希望能够“设置几个小时的睡眠时间,如果这是它确定应该做的事情".
  • @DaveSherohman 我也是这么想的,但选择这个是为了便于阅读和代码紧凑。
  • 这正是我不想做的。我想要一个可以持续几个小时的睡眠,并且我想编写脚本在被告知时立即停止。换句话说,我想打断睡眠。
  • @jurgen 然后采用我提到的另一种方法,结合SIGALRM,因为这会中断 sleep()。相应地编辑了我的答案。
  • 编辑您的启动/停止脚本以发送 SIGALRM,并编辑此脚本以执行此操作(除了您已有的脚本之外)
【解决方案3】:

除非你做了一些奇怪的事情来改变它,否则sleep() 函数应该提早唤醒并在收到信号时立即返回。然后,您可以更改循环以在每次睡眠后检查 $continue 标志,以查看您是否收到关闭信号。

【讨论】:

  • 它自己醒来了。哇,我现在觉得自己很愚蠢。您不会碰巧知道如何杀死我以 system() 调用开始的程序,不是吗?也许我在那里也忽略了一件简单的事情。
  • @jurgen - 同样的方式:发送一个信号。 SIGINT(“中断”,相当于按 Ctrl-C)将是最明显的候选者,或者 SIGKILL 如果您需要它现在而没有机会进行任何清理(可能是个坏主意)。
  • @jurgen system 不会返回,直到您启动的程序已经 exited。所以你不能停止一个你从它开始的程序,因为它已经停止了。
  • 我想过使用 kill() 来杀死程序,但我确实需要 PID 来执行此操作,并且系统不会返回它。经过一番谷歌搜索后,我发现了一些使用 fork+exec 技巧来获取 PID 的代码。还没有测试它,但它看起来不错。基本上你只需 fork() 并在 if 中返回子 PID,else 块执行 exec()。然后调用者可以使用 waitpid() 阻塞直到完成。当信号进来时,您可以杀死 pid。我希望它会起作用,将在星期一进行测试。