#!D:/Perl/bin/perl.exe #!/usr/local/bin/perl #--------------------------------------------------------------------------------- # Windows用不良Perlプロセス削除スクリプト # URL:http://sakaguch.com 電子メール:http://sakaguch.com/cgi/postmail/ より送信して下さい。 # ファイル名:perl_kill.pl 2004.11.22 作成:鷹の巣 #--------------------------------------------------------------------------------- # 改版記録: # 2004.11.21 Rev.1.000 初版で公開。 # 2004.11.22 Rev.1.001 不良プロセスの判定時間(分)の1~9を1~99の範囲に変更。 # 2004.12.08 Rev.1.002 不良Perlプロセス削除スクリプトの停止時刻動作のバクを訂正。 $ver = 'perl_kill.pl Rev.1.002(作成:鷹の巣)'; #--------------------------------------------------------------------------------- # 1.用途 # #  このPerlスクリプトは、フリーソフトです。詳細は、項4をご一読願います。 # #  このPerlスクリプトは、WebサーバーのCGIとして実行されたPerlのプロセスで、 #  ゾンビプロセスと化したPerlのプロセスを消去します。 # #  ブラウザがアクセス途中で読込みを中断した場合は、errorcode 32のbroken pipeが #  発生します。それが標準入出力を使用したCGIのPerlスクリプトの実行中であった場合、 #  WindowsのPerlのプロセスは、ゾンビプロセスになることがあります(と思います)。 # #  従って、このPerlスクリプトは、broken pipe処理の不完全なOSであるWindowsに #  特化しています。 # #--------------------------------------------------------------------------------- # 2.このスクリプトの特徴 # #  Perlのプロセスの一括削除だけでしたら、Windowsのバッチファイル内にshellスクリプトを #  書くだけで実現できます。 #  参考URL:AN HTTPD ゲストブック/コメント集(2004年11月16日03:39) #       http://homepage1.nifty.com/yito/namazu/gbook/20041116.0339.html # #  このスクリプトは、上記のshellスクリプトをPerlのスクリプトに改造したものです。 # #  Windowsのバッチファイルに比べて以下の特徴があります。 #  ○利点:特定のプロセスを削除しない様に設定できる。 #      ログファイルが綺麗で、解析がしやすい。 #      ログファイルとWebサーバーのログファィルから、 #      削除されたプロセスのCGIの種類を判別できる。(詳細は7-2を参照) #      Perlのスクリプトなので、ソースが読みやすい? # #  ○欠点:設置するのが難しいかもしれません。 # #--------------------------------------------------------------------------------- # 3.ご注意事項 # #  このスクリプトは、Windows 2000 Professional #  Active Perl 5.8.4.810 built for MSWin32-x86-multi-thread #  の環境にて、動作確認を行っております。 # #  このスクリプトに対するご質問や不具合がございましたら、電子メールで、 #  webmaster@sakaguch.com まで、お寄せ下さい。 #  また、「鷹の巣」の自宅サーバー掲示板 #  http://sakaguch.com/cgi/bbs/ #  にご投稿して頂いても結構です。 # #  ☆★☆「いとのページ」さんとは、全く無関係ですから、☆★☆ #  ☆★☆「いとの掲示板」へは、絶対にご質問しないで下さい。☆★☆ # #--------------------------------------------------------------------------------- # 4.著作権 # #  このスクリプトで、鷹の巣が改造した箇所の著作権は、放棄していますが、 #  このファイルは、下記のWebページを参考(基底)にして作成しました。 #  ○AN HTTPD ゲストブック/コメント集(2004年11月16日03:39) #   http://homepage1.nifty.com/yito/namazu/gbook/20041116.0339.html #  ○AN HTTPD 自動再起動プログラム anhttpd_restart #   http://homepage1.nifty.com/yito/anhttpd/anhttpd_restart.html #  このスクリプトは、anhttpd_restart.htmlのanhttpd_restartXXX.lzhに同梱されている #  readme.txtに従って再配布するものです。ご利用に当たっては、ご留意願います。 # #  スクリプトの再配布や改造は自由ですが、無償として下さい。 #  いかなる目的であっても、このスクリプトに付加価値をつけて、 #  有償配布してはなりません。 # #--------------------------------------------------------------------------------- # 5.設置例 # #  (1)プロセス一覧表示コマンド(pslist.exe)とプロセス削除コマンド(pskill.exe)を #    下記のURLのPstools.zipをダウンロードして、入手してください。 #     http://www.sysinternals.com/ntw2k/freeware/pstools.shtml #  (2)Pstools.zipをWebサーバー機内の適当なディレクトリに解凍して下さい。 #  (3)解凍したディレクトリに本スクリプトのperl_kill.plを設置して下さい。 # #    D:\bin\Pstools (解凍ディレクトリ)---実行ユーザーでの実行権限と書き込み権限が必要 #         | #           \pslist.exe #           \pskill.exe #           \perl_kill.pl   ※このスクリプトファイル自身。 #           \PIDsafeList.txt ※参照するプロセス削除除外ファイル。(空ファイルでも可) #           \PIDkilled.log  ※出力するログファイル。 # #--------------------------------------------------------------------------------- # 6.起動方法 # #  (1)コマンドプロンプト画面(DOS窓)にて、perl_kill.plのあるPstools(解凍ディレクトリ) #   に移動し、コマンドラインから、 #     perl perl_kill.pl #   を実行します。 #  (2)または、perl_kill.plのショートカット(アイコンリンクの様なもの)を作成して、 #   これをタスクスケジューラに登録して、一日に一回定時に起動します。 #   他にPerlの常駐プロセスがある場合は、この方式になります。 #    #   タスクスケジューラの例) #    a)毎朝、4時40分に「プロセス削除除外ファイル(PIDsafeList.txt)」を空ファイルにする。 #    b)毎朝、4時41分から、他のPerlの常駐プロセスを起動して、PIDsafeList.txtに #     常駐プロセス番号を自動追記する。 #    c)毎朝、4時45分に本Perlスクリプト(不良Perlプロセス削除スクリプト)を起動する。 #    d)本Perlスクリプトや他のPerlの常駐スクリプトは、毎朝、4時30分前に #     終了する様に設定しておく。 # #--------------------------------------------------------------------------------- # 7.その他 # #  (1)このPerlスクリプトは、WebサーバーのCGIとして実行されたPerlのプロセスと、 #    他の常駐Perlプロセスとの識別に「プロセス削除除外ファイル(PIDsafeList.txt)」を #    使用します。一般の用途では、空ファイルでも結構です。 # #    このファイルには、削除したくないプロセス番号を1行に1個記入しておきます。 #    例)削除したくないperlの常駐プロセスがある場合は、 #      その常駐プロセスのperlスクリプトの先頭近くに #      $PIDsafeFileName = "D:/bin/Pstools/PIDsafeList.txt"; # open(OUT,">>$PIDsafeFileName") || print LOG "$PIDsafeFileNameオープン失敗\n"; # print OUT "$$\n"; # $$には、自己のプロセス番号が格納されています。 # close(OUT); #     と追記しておきます。 # #  (2)ログファイルの見方 #    WebサーバーのCGIとして実行されたPerlのプロセスで削除されたプロセスは、 #    以下の書式で記録されています。 #    2004.11.21 21:44:06 ElapsedTime=0:01:06.004 Name=perl killed Pid=292(SafePID=XXXX XXXX ) Pri=8 Thd=1 Hnd=16 Priv=592 CPUtime=0:00:00.060 # #    記録時間の2004.11.21 21:44:06から、ElapsedTimeの0:01:06.004を差し引いた時刻が #    削除されたプロセスの起動時刻になりますので、この時刻からWebサーバーのログファィルを #    見て、削除されたプロセスのCGIの種類を判別して下さい。 # #  (3)PsList実行で、Process performance object not foundが発生する場合。 # #    Windows 2000 Professionalで、pslist.exeを実行すると、 #    D:\bin>pslist.exe # #    PsList 1.26 - Process Information Lister #    Copyright (C) 1999-2004 Mark Russinovich #    Sysinternals - www.sysinternals.com # #    Process performance object not found on ログ中のユーザ名 #    Run Exctrlst from the Windows Resource Kit to repair the performance counters. # #    と言うエラー表示が出る場合は、 #    ftp://ftp.microsoft.com/reskit/win2000/exctrlst.zip #    から、exctrlst.zipをダウンロードして解凍し、exctrlst.exeを実行して、 #    PerfProc perfproc.dllの行が反転する様にして、 # #    レ   Performance Counters Enabled # #    という様にPerformance Counters Enabledにチェックを入れ「Refresh」ボタンを押すと、 #    正常にpslist.exeが実行できる様になります。 # #    こちらのサイト #    http://www.geocities.co.jp/SiliconValley/6705/other/aassdd.txt #    を参考にさせて頂きました。 # #--------------------------- #■□■ 基本設定 ■□■ #--------------------------- # ■ 監視時間間隔(秒) $check_interval = 258; # ■ 不良プロセスの判定時間(分)を1~99の範囲で設定 $KillTiming = 10; # ■ 不良Perlプロセス削除スクリプトの停止時刻 # このスクリプトを連続起動として終了させない。 $StopHour = 25; $StopMinute1 = 0; $StopMinute2 = 0; # このスクリプトをタスクスケジューラから起動した場合等に、毎時4時15~30分に終了する。 # $StopHour = 4; $StopMinute1 = 15; $StopMinute2 = 30; #------------------------------- #■□■ 基本設定の終わり ■□■ #------------------------------- #-------------------------------------------- # これより下は通常、変更する必要はありません。 #-------------------------------------------- # ■ 不良プロセスの種類 $TargetProcessName = "perl"; # ■ プロセス一覧表示コマンド $CmdPsList = "pslist.exe $TargetProcessName"; # ■ プロセス削除コマンド $CmdPIDkill = "pskill.exe"; # ■ プロセス削除除外ファイル # 削除したくないプロセス番号を1行に1個記入しておく。 # 例)削除したくないperlの常駐プロセスがある場合は、 #   そのperlスクリプトの先頭近くに # $PIDsafeFileName = "X:/~/Pstools/PIDsafeList.txt"; # open(OUT,">>$PIDsafeFileName") || print LOG "$PIDsafeFileNameオープン失敗\n"; # print OUT "$$\n"; # $$には、自己のプロセス番号が格納されています。 # close(OUT); #   と追記しておきます。 $PIDsafeFileName = "./PIDsafeList.txt"; # ■ ログファイル(実行ユーザーでの書込み権限が必要) $LogFileName = "./PIDkilled.log"; &main(); exit; #-------------------------------------------- # 主回路 #-------------------------------------------- sub main{ # 改版番号の表示 print "$ver\n\n"; open(LOG,">>$LogFileName") || print "$LogFileNameオープン失敗\n"; # 本スクリプトのPID番号(プロセス番号)を$MyPIDnoに格納する。 $MyPIDno = "$$"; $MatchTime = sprintf("0:%02d:", $KillTiming); for (;;){ # 無限ループ # 処理の開始時間から次回の処理開始時間を算出する。 $start_time = time(); $next_time = $start_time + $check_interval; # killしない常駐PID番号データの作成 ($SafePIDrecordData , @PIDsafeList) = &MakeSafePIDdata ($MyPIDno); # 外部コマンドのプロセスリストを実行して、その結果を配列@PsListに格納する。 open(PS_LIST,"$CmdPsList |") || print LOG "$CmdPsListオープン失敗\n"; @PsList = ; close(PS_LIST); # 全perlプロセスについて不良プロセスを検査する。 foreach (@PsList) { chomp; # perlプロセスの記述行を検査する。 if (/$TargetProcessName\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/) { $Pid = $1; $Pri = $2; $Thd = $3; $Hnd = $4; $Priv = $5; $CPUTime = $6; $ElapsedTime = $7; # このスクリプトでkillしない常駐PID番号(常駐プロセス番号)を検査する。 $FlagSkip = 0; foreach $PIDsafe (@PIDsafeList) { chomp ($PIDsafe); if ($Pid == $PIDsafe) { $FlagSkip = 1;last; } } # 常駐PID番号でなくて、不良プロセスなら、そのプロセスを削除する。 if (not $FlagSkip and $ElapsedTime =~ /$MatchTime/) { # プロセスを削除する。 system ("$CmdPIDkill $Pid"); # 削除した不良Perlプロセスの記録と表示。 $date_now = &GetDate; $LogData = "$date_now ElapsedTime=$ElapsedTime\tName=$TargetProcessName\tkilled Pid=$Pid($SafePIDrecordData)\tPri=$Pri\tThd=$Thd\tHnd=$Hnd\tPriv=$Priv\tCPUtime=$CPUTime"; print LOG "$LogData\n"; print "$LogData\n"; } } } # 毎日定時前に終了する。 $date_now = &GetDate; if (($hour == $StopHour) && ($min > $StopMinute1) && ($min < $StopMinute2)) { last; } # 余った時間の処理を休止する。 while (time() < $next_time) { sleep (1); } } close(LOG); } #-------------------------------------------- # killしない常駐PID番号データの作成 #-------------------------------------------- sub MakeSafePIDdata { my $MyPIDno = $_[0]; my @PIDlist , $PIDdata , $_ ; # このスクリプトでkillしない常駐PID番号(常駐プロセス番号)を配列@PIDlistに格納する。 open(IN,"$PIDsafeFileName") || print LOG "$PIDsafeFileNameオープン失敗\n"; @PIDlist = ; close(IN); # このスクリプトでkillしない常駐PID番号配列@PIDlistの最初に本スクリプトのPID番号を追加する。 unshift ( @PIDlist, $MyPIDno); # 常駐PID番号記録データの作成。 $PIDdata = "SafePID="; foreach (@PIDlist) { chomp; $PIDdata .= "$_ "; } return ( $PIDdata , @PIDlist); } #-------------------------------------------- # 現在時刻の取得 #-------------------------------------------- sub GetDate { ($sec, $min, $hour, $day, $month, $year) = localtime(time); $year += 1900; $month ++; return sprintf("%04d\.%02d\.%02d %02d:%02d:%02d", $year, $month, $day, $hour, $min, $sec); }