对system()的调用不会在Windows中的进程之间创建父子关系

时间:2012-04-20 09:45:41

标签: windows perl process windows-services

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

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

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

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

编辑:我添加了上面的代码。我只显示了调用服务回调并调用StartService的主函数,以及三个主要的回调:start,running,stop。

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 个答案:

答案 0 :(得分:2)

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

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

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

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

答案 1 :(得分:2)

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

我不确定从Perl做起来有多容易,但从C开始并不难。看起来有一些CPAN模块可以帮助你解决这个问题,例如{{3} }和Win32::Job,但我自己没有经验。

答案 2 :(得分: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)已终止。
  6. 创建守护进程时常见的习惯用法是fork,然后让父进程死掉,以便子进程(实际守护程序进程)保持父进程。这避免了守护进程仍然与控制终端连接的问题。

答案 3 :(得分: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);         

希望这可以提供帮助