我正在使用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();
}
答案 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))已经死亡。
(已更新,以包含可能发生此情况的具体方案)
一种情况:
创建守护进程时常见的习惯用法是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);
希望这可以提供帮助