1

我正在使用 Perl 创建 Windows 服务。我正在Win32::Daemon为此目的使用。

处理服务的 Perl 脚本(接受启动和停止回调等)使用system()命令调用 .bat 文件,最终调用我的最终 Perl 程序。

问题是,当我停止服务时,由 启动的进程system()没有关闭,最终进程也没有关闭(由 生成的进程启动system())。

这就像进程之间没有“父子”关系(停止Windows服务通常会导致所有相关进程同时关闭)。

编辑:我添加了上面的代码。我刚刚展示了注册服务回调和调用 StartService 的主函数,以及三个主要回调:启动、运行、停止。

sub main {
        #registering service callbacks
        Win32::Daemon::RegisterCallbacks( {
            start       =>  \&Callback_Start,
            running     =>  \&Callback_Running,
            stop        =>  \&Callback_Stop,
            pause       =>  \&Callback_Pause,
            continue    =>  \&Callback_Continue,
         } );
        my %Context = (
            last_state => SERVICE_STOPPED,
            start_time => time(),
      );

      Win32::Daemon::StartService( \%Context, 2000 );

      # Here the service has stopped
      close STDERR; close STDOUT;
}


#function called after the start one
sub Callback_Running
{
   my( $Event, $Context ) = @_;

#Here I had to make an infinite loop to make the service "persistant". Otherwise it stops automatically (maybe there's something important I missed here?

   if( SERVICE_RUNNING == Win32::Daemon::State() )
   {
       Win32::Daemon::State( SERVICE_RUNNING );

   }
}   

#function first called by StartService
sub Callback_Start
{

#above is stated the system() call where I trigger the .bat script

       my( $Event, $Context ) = @_;

        my $cmd = "START \"\" /Dc:\\path\\to\\script\\dir\\ \"script.bat\"";

    print $cmd."\n";
    system($cmd);

   $Context->{last_state} = SERVICE_RUNNING;
   Win32::Daemon::State( SERVICE_RUNNING );
}

sub Callback_Stop
{
   my( $Event, $Context ) = @_;

   #Things I should do to stop the service, like closing the generated processes (if I knew their pid)

   $Context->{last_state} = SERVICE_STOPPED;
   Win32::Daemon::State( SERVICE_STOPPED );

   # We need to notify the Daemon that we want to stop callbacks and the service.
   Win32::Daemon::StopService();
}
4

4 回答 4

2

在 Windows 中,进程之间没有父子关系。Windows 将所有进程视为对等进程,尤其是杀死“父进程”永远不会杀死“子进程”。

当服务收到停止请求时,它负责关闭/杀死它可能已经创建的任何进程,如果这样做合适的话。

您说“停止 [原文如此] Windows 服务通常会导致所有相关进程关闭”,但通常情况并非如此;也许 Win32::Daemon 正在为您执行此操作,但是它当然无法知道从您的批处理文件启动的任何进程。

如果可能的话,正确的解决方案是在单个进程中实现您的服务。由于在这种情况下,两个进程都是用 Perl 编写的,因此将它们组合成一个程序应该相对简单。

于 2012-04-22T00:32:42.863 回答
2

Harry Johnston 的回答基本上是正确的。如果很难跟踪“孩子”,您可以做的一件事是将您创建的所有进程放入Windows Job对象中,而不是通过 启动它们system,然后在进程的服务关闭代码中终止该作业.

我不确定从 Perl 中做到这一点有多容易,但从 C 中来说并不太难。看起来确实有一些 CPAN 模块可以帮助您解决这个问题,例如Win32::JobWin32: :JobAdd,但我自己没有任何经验。

于 2012-04-22T02:17:30.160 回答
1

如果批处理文件在最终的 Perl 程序之前终止,那么最终的 Perl 程序将没有父程序,因为它的父程序(批处理文件的命令处理器(如 cmd.exe))死了。

(更新以包括可能发生这种情况的特定场景)

一种情况:

  1. 批处理文件 B 启动 Perl 程序 P1。
  2. Perl 程序 P1 启动 Perl 程序 PFINAL。
  3. Perl 程序 P1 无需等待 PFINAL 终止就终止。
  4. 批处理文件 B 继续执行下一个命令,因为 P1 已终止。
  5. 如果批处理文件 B 然后在 PFINAL 终止之前终止,那么 PFINAL 就没有父级了,因为它的父级(Perl 程序 P1)和它的祖父级(批处理文件 B)都已终止。

创建守护进程时的一个常见习惯用法是分叉,然后让父进程死亡,这样子进程(实际的守护进程)就没有父进程了。这避免了守护进程仍与控制终端连接的问题。

于 2012-04-20T16:02:16.590 回答
1

我有同样的问题。

我的 perl 脚本调用 java 应用程序,同时应用程序应该被杀死。

我用Win32::Process了模块

Win32::Process::Create($ProcessObj,
    "C:/Java/bin/java.exe",
    "java.exe -cp bin/blablabla.jar",
    0,
    CREATE_NEW_CONSOLE,
    ".")

$ProcessObj->GetExitCode($retCode);         
$ProcessObj->Kill($retCode);         

希望这可以帮助

于 2012-05-21T14:50:31.747 回答