1

私はPerlを使用してWindowsサービスを作成しています。私はWin32::Daemonこの目的のために使用しています。

サービスを処理しているPerlスクリプト(開始および停止コールバックなどを受け入れる)は、system()コマンドを使用して.batファイルを呼び出し、最終的には最終的なPerlプログラムを呼び出します。

問題は、サービスを停止すると、によって起動されたプロセスがsystem()閉じられず、最終プロセス(によって生成されたプロセスによって起動された)も閉じられないことsystem()です。

これは、プロセス間に「親子」関係がないようなものです(Windowsサービスを停止すると、通常、関連するすべてのプロセスが同時に閉じられます)。

編集:上記のコードを追加しました。サービスコールバックを登録してStartServiceを呼び出すメイン関数と、start、running、stopの3つのメインコールバックを表示しました。

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 で記述されているため、それらを 1 つのプログラムに結合するのは比較的簡単です。

于 2012-04-22T00:32:42.863 に答える
2

ハリー・ジョンストンの答えは基本的に正しいです。「子」を追跡するのが難しい場合にできることの 1 つは、作成したすべてのプロセスを経由で起動する代わりにWindows ジョブオブジェクトに配置systemし、プロセスのサービス シャットダウン コードでジョブを強制終了することです。 .

Perl から実行するのがどれほど簡単かはわかりませんが、C から実行するのはそれほど難しくありません。Win32::JobWin32 など、これを支援する CPAN モジュールがいくつかあるようです: :JobAddですが、私自身は経験がありません。

于 2012-04-22T02:17:30.160 に答える
1

バッチファイルが最終的なPerlプログラムの前に終了した場合、その親(バッチファイルのコマンドプロセッサ(cmd.exeなど))が停止したため、最終的なPerlプログラムには親がありません。

(これが発生する可能性のある特定のシナリオを含むように更新されました)

1つのシナリオ:

  1. バッチファイルBはPerlプログラムP1を起動します。
  2. PerlプログラムP1はPerlプログラムPFINALを起動します。
  3. PerlプログラムP1は、PFINALが終了するのを待たずに終了します。
  4. P1が終了したため、バッチファイルBは次のコマンドに進みます。
  5. PFINALが終了する前にバッチファイルBが終了した場合、その親(PerlプログラムP1)とその祖父母(バッチファイルB)の両方が終了したため、PFINALは親なしのままになります。

デーモンを作成するときの一般的なイディオムは、フォークしてから親を死なせて、子(実際のデーモンプロセス)を親なしのままにすることです。これにより、デーモンプロセスがまだ制御端末に接続されているという問題を回避できます。

于 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 に答える