• 締切済み

バックグラウンド処理をforkする意味

初歩的な質問かもしれませんが… デーモンなどではなく、あくまでも通常のCGIスクリプトにおいて、何か重い処理をさせる場合は、STDOUTをcloseして処理はforkで生成した子プロセスにさせる、するとユーザーを待たせることなく(ブラウザをビジーから開放し)処理はバックグラウンドで実行される、という方法がよく見られるのですが、素朴な疑問があります。 ブラウザを開放させるにはSTDOUTをcloseすれば良い、 では、なぜ処理を子プロセスにさせなければならない? という点です。 一応、以下のような簡単なコードで試してみました。 ----------------- use Date::Format; my $log_file = 'hogehoge/test.log'; my $start = time2str('%m-%d %T', time); print "Content-Type: text/plain\n\n"; print "start:$start"; close(STDOUT); &heavy_exe($start); exit; sub heavy_exe { my $start = $_[0]; sleep(15); my $now = time2str('%m-%d %T', time); open(LOG, ">>$log_file") or &error('cant_open'); print LOG "$start\t$now\n"; close(LOG); } ----------------- 実行してみると、ブラウザはすぐさま開放される(読み込み完了になる)のと、15秒後にロギングも問題なく行われ、また本当に開放されていることを確認するために表示から15秒以内にリロードさせても、問題なく2プロセスとも動いていました。 以上を見る限りでは、forkなんてしなくても単にブラウザを開放するだけでいい、STDOUTをcloseするだけでバックグラウンド処理されると思うのですが、どうなのでしょう。 あるいは何か環境に依存した、子プロセスにさせなければならない理由などがあるのでしょうか。 よろしくお願いいたします。

  • CGI
  • 回答数2
  • ありがとう数0

みんなの回答

回答No.2

ログ出力された=実行できた というのは間違いではありませんが、 ログ出力されたのをどうやって判定したかということです。 別にコンソールかなにかで、探ってみたということですよね? 同プログラム内で、処理が返ってきてその結果を受けた訳ではないですよね? と、いうことが言いたかっただけです。 ただ、標準出力を開放してしまいますと、 後に実行しているプログラムが無限ループに陥っても分らなくなるので、少々危険だと思われますし、 上記のプログラムでは、ブラウザを数回リロードされると その分実行されるので、サーバ内部では結局重い処理の連続になると思われます。くれぐれもご注意を。

taseki
質問者

補足

たびたびありがとうございます。 > ログ出力されたのをどうやって判定したか もちろんログそのものを見て判定しました。 ご覧のとおりプログラム自体は処理結果を返しませんから、ログ出力されたのを確認するにはログを見るしかありません。なので、heavy_exe が実行されたかどうかの判定は問題ないと思います。 どうも私の質問の仕方が悪かったようですね。すみません。 標準出力の開放、という部分が解らないのですが(逆にcloseしています)、いずれにしても、お聞きしたかったのは、このプログラムのロジックうんぬんではないんです…(単なる実験用コードです)。 「perl fork バックグラウンド」などで検索してもらうといくらでも見つかりますが、よくあるパターン「STDOUTをcloseしてforkで生成した子プロセスにバックグラウンド処理させる」という広く知られた方法がありますよね。これについて、「STDOUTをclose」は解りますが「子プロセスにバックグラウンド処理させる」理由が知りたかったのです。 標準出力をcloseするとhttpdがスクリプトのプロセスをkillしようとするから、という記事も見つけましたが、試してみたところ10分たってもプロセスは生きていました。もしかしたらバージョンによる違いかもしれませんが(apache2系)。 どなたか、御存じないでしょうか。

回答No.1

念のため、バックグランウンド処理 == fork ではありません。 ブラウザに開放だけですと、 close(STDOUT); でいいように思います。 上記プログラムですと、 なお、heavy_exe が本当に実行できているかは判断がつかないように思います。 # 違う重い処理を書いた場合、ひょっとしたら、ゾンビってるかもしれませんし。 forkはそもそも、子プロセスの挙動を制御(返り値が取れる)するためで、daemon系のプログラムに有用でしょう(今日、threadの方がいいでしょうけど) # forkを使ってもゾンビるときはゾンビますけど。 私自身は、forkは嫌いなので、重い処理があるときは、 予約データとして値を何かしらに格納し、別プログラムをcronで走らせてます。

taseki
質問者

補足

早速のご回答ありがとうございます。 誠にすみません、ちょっと意味が解らなかったのですが… 結論としては、私の推測は正しかった、という理解でいいのでしょうか? また、 > heavy_exe が本当に実行できているかは判断がつかない 上記について、よろしければもう少し詳しく御教授いただけないでしょうか。 ログ出力された=実行できた、と考えたのですが、これは間違いと言うことですよね?

関連するQ&A

  • forkしてもバックグラウンドで動かない

    お世話になっております。 あるサイトで見たforkの例を元に下記のようなソースでバックグラウンドで処理 しようとしているんですが、処理が終わるまでブラウザがロックされて しまします。 $| = 1; rm("-f","$TmpPath$KYOTEN/result/*.*"); &Upload_Check; print "Content-type: text/html\n\n"; &DataProgHeader_Write; #画面の切替 &DataProgDetail_Write; FORK: { if( $pid = fork ) { close(STDOUT); wait; } elsif (defined $pid) { close(STDOUT); chdir "/u1/uca/htdocs/PPro"; system("perl U0302.pl $Dkyoten"); exit; } elsif ( $! =~ /No more process/) { sleep 5; redo FORK; } else { &MsgDisp("Forkできませんでした。"); } } # End Of Label:FORK バックグラウンドで動かない理由が分からないのです。 どなたかお助けください。 また、ソース中で system("perl U0302.pl $Dkyoten"); とありますが、サーバで直に動かすと正常に動作するのですが cgiから呼び出すと文字コードエラーで落ちてしまします。 (呼び出すCGIはEUCで書いていますが、U0302.PLは処理の都合上 SJISで書いています。) こちらも原因の想定がつきません。 どなたか参考サイトでも構いませんので、お教えください。 お願いいたします。

    • ベストアンサー
    • Perl
  • IIS7.0でPerlのCGIにてバックグラウンド

    はじめまして、初心者の質問で恐縮ですが、以下の点を教えて欲しいです。 IIS7.0のWebサーバで、CGIプログラムをPerlで作っているのですが、forkした子プロセス で時間のかかるバックグラウンド処理を行って、親プロセスでWebブラウザに返す処理を しようとしていますがうまくいきません。重たい処理が終了するまでWebブラウザに応答 されません。(子プロセスの終了が終わるまで、親プロセスのHTML表示処理がWebブラ ウザに返答されません)通常、子プロセスでclose(STDOUT)で親プロセスのHTMLの 表示処理が出来ると思っていますが。。。 どのようにすれば良いか教えてもらえると幸いです。以下は参考のプログラムです。 よろしくお願いします。 $|=0; if ( $pid = fork){ #親プロセス &disp_html(); #HTMLを表示させるプログラム wait; }elsif (defined $pid) {  #子プロセス close(STDIN); close(STDOUT); close(STDERR); &heaby_prog(); #重たい時間のかかる処理 exit 0; } else { die "Can't fork: $\n"; } よろしくお願いします。

  • 標準出力と標準エラー出力を時系列にファイルへ

    例として、perlなどで、(test.plとします)  print "stdout1\n";  print STDERR "STDERR1\n";  print "stdout2\n";  print STDERR "STDERR2\n";  print "stdout3\n";  print STDERR "STDERR3\n"; このように、標準出力と、標準エラー出力が混在した状態の処理があった場合、 コマンドプロンプト(Windows2000)にて、 C:\>test.pl とすると、 stdout1 STDERR1 stdout2 STDERR2 stdout3 STDERR3 のように時系列に出力されますが、これをログファイルに取ろうとして、 C:\>test.pl 1>log.txt 2>&1 とすると、 C:\>cat log.txt STDERR1 STDERR2 STDERR3 stdout1 stdout2 stdout3 のように、標準エラー出力が先に吐き出されてしまいます。 これを画面出力時と同様に時系列で取れるようにしたいのですが、どのようにすれば良いでしょうか? 単純なことで困っています。よろしくお願いします。

  • Perlの多重起動を禁止したい

    main_01.plというプログラムがあります。 これは10分毎にバッチファイルから起動の命令が下ります。ただしプログラムの処理によっては10分以上かかることがあります。 この時、main_01.pl がすでに起動していた場合、main_01.pl はすぐに exit; になるように組みたいのです。僕が考えた方法は、あるテキストファイルにロックをかけ、そのファイルがロック中であるならばプログラムは処理を行わずすぐに終了する、といった感じです。 =============================== # タイムアウト処理 my $timeout = 5; # 排他処理 my $lockfile="lock.txt"; # 多重起動停止 (ここに $lockfile が書き込めなかったらmain_01.plは別に起動していることになるので起動しない。) # 排他処理開始 open(LOCK,"$lockfile"); flock(LOCK,2); # 処理開始 eval { # タイムアウト local $SIG{ALRM} = sub { die "alarm\n" }; alarm( $timeout ); # プログラムスタート print "\n◆スタート\n\n"; sleep(4); # プログラム終了 print "\n◆終わりました。\n\n"; }; # タイムアウト処理 if ( $@ ) { # タイムアウト print "\nタイムアウトしました。TIMEOUT= $timeout \n"; } else { # タイムアウトせずに正常終了 print "\n正常終了しました。\n"; } # 排他処理終了 close(LOCK); exit;

    • ベストアンサー
    • Perl
  • python: fork後の標準出力について

    言語はpythonです。(バージョン : 2.6.6) 以下の二つの単純なpythonスクリプト   test.py, exe_fork.py があります。 [test.py] #!/usr/bin/env python import subprocess proc = subprocess.Popen("./exe_fork.py", stdout=subprocess.PIPE) result = proc.stdout.read() print result [exe_fork.py] #!/usr/bin/env python import os import time pid = os.fork() if pid != 0: print "parent\n" else: time.sleep(5) print "child\n" [動作] 各スクリプトの動作としては、 test.pyがexe_fork.pyをPopen(stdoutはPIPEで受信)で実行し、 read()メソッドでexe_fork.pyの標準出力を受け取り、表示する。 exe_fork.pyはforkして、   親プロセスは"parent"と出力   子プロセスは5秒後に"child"と出力 です。 [実行結果] test.pyを実行すると 5秒後に   parent   child と表示されました。 これを実行する前は実行後すぐに "parent" と表示されてスクリプト終了するのだと予想しておりました。 しかし実際はexe_fork.pyの親と子の両方のプロセス終了するまで何も表示されず 両プロセスが終了した後にtest.pyのread()メソッドが完了するようです。 (どういうしくみでそうなっているのかよくわかりません。。) [質問・相談] test.pyでexe_fork.pyの親プロセスの標準出力だけを反映する方法ございませんでしょうか? なお、都合上、以下の条件を満たしている必要がございます。  条件1: test.py側のソースは変更しない  条件2: exe_fork.pyの親プロセスが終了した時点でtest.pyのread()メソッドが終了する  条件3: forkptyは使わない [参考]  exe_fork.py側で子プロセスのみsys.stdout.close()してみましたが結局exe_fork.pyの子プロセスが終了するまで(=5秒経過するまで)test.pyのread()メソッドは完了しませんでした。   よろしくお願いします。

  • 処理能力計測について

    現在処理能力を計算するプログラムを組んでいます。 time_t start,end; : start=time(NULL); 時間を計測したい処理 end=time(NULL); printf("time %.0f s\n",difftime(end,start)); このように書いているのですが、出力は秒単位になります。もっと細かい単位で計りたいのですが、どうしたらよろしいでしょうか? よろしくお願いします。

  • Perlの日付取得で月の表示がおかしい

    下記は、メールフォームCGIに書き込まれた日時などの情報をlog.cgiというファイルに書き出すスクリプトです。 2012/01/04 21:05:45 のような感じで、日付が刻まれるのですが、このうち月の部分がどの月に処理を行ってもいつも01になってしまうのですが、当方直し方がわかりません。 下記が実際の記載箇所です。4行目がおかしいのだと思いますが、どなたかお分かりの方、教えていただけないでしょうか。 my $path = "log.cgi"; my $ip = $ENV{'REMOTE_ADDR'}; ($sec, $min, $hour, $mday, $month, $year, $wday, $stime) = localtime(time()); my $time = sprintf("%04d\/%02d\/%02d %02d\:%02d:%02d", $year + 1900, $mon + 1, $mday, $hour, $min, $sec); my $message = $in{'メッセージ'}; $message =~ s/\r\n/ /g; $message =~ s/\n/ /g; if (open(FOUT, ">> $path")) { print FOUT "$time\t$ip\t" . $in{'お名前'} . "\t" . $in{'E-mail'} . "\t" . $message . "\r\n"; close(FOUT); } よろしくおねがいいたします。

    • ベストアンサー
    • Perl
  • 連続投票の制限

    投票cgiを設置したいのですがうまくいきません、集計を1日一回だったのを少し改造して即時集計にしました 一応動くのですが、連続投票できてしまいます。 連続投票を一定時間制限できるようにしたいのですがうまくいきません 60秒くらい間をおいてから投票できるようにできますか? ip制限もあるみたいなのですがこれも機能してないです・・・ 改善できるなら元のソースがかなり変わってもかまいません 連続投票制限だけでも機能できればと思っています。よろしくお願いします local $times = time(); sub vote{ if($FORM{id}){ my $fl=0; if(!$CK_ref){$fl=1;} else{ if(index($ENV{'HTTP_REFERER'},index.html>=0){$fl=1;} } if($fl){ $FORM{id}=~s/\n//g; my $vote = $FORM{vt2}?1:0; open(OUT,">>vote_temp.cgi"); #時間用のログファイルに書き込み print OUT "$FORM{id}<>$ENV{'REMOTE_ADDR'}<>$FORM{vt}<>$vote<>".$times."<>\n"; close(OUT); &reset_vote if $VT_RESET < $times; my(@log_lines,%pt,%pt2,%cnt,$name,$value,%CKIP); open(IN,"vote_temp.cgi"); my @log = <IN>; close(IN); if(@log){ my $cktime = $times - 60; #60秒制限 open(IN,"vote_ck_IP.cgi"); #投票した人のIPを記録したログ while(<IN>){ my @ck = split('<>'); next if $_[1] < $cktime; $CKIP{$_[0]} = $_[1]; } close(IN); foreach(@log){ chop; my @rank = split('<>'); #ID<>IP<>評価<>おすすめ<>時間\n next if $CKIP{"$rank[0]_$rank[1]"}; $pt{$rank[0].'_'.$rank[2]}++; $pt2{$rank[0]}++ if $rank[3]; $cnt{$rank[0]}++; $CKIP{"$rank[0]_$rank[1]"} = $rank[4]; } open(OUT,">vote_temp.cgi"); close(OUT); open(OUT,">vote_ck_IP.cgi"); while(($name, $value) = each(%CKIP)){ print OUT "$name<>$value<>\n"; } close(OUT); open(IN,"log.cgi"); my @data = <IN>; close(IN); foreach(@data){ #集計処理長かったので省略 } open(OUT,">vote_bf.cgi"); open(IN,"vote_log.cgi"); while(<IN>){ print OUT $_; } close(IN); close(OUT); open(OUT,">vote_log.cgi"); print OUT @log_lines; print OUT "\n1;\n"; close(OUT); } &make_vote_ck('set'); } } sub reset_vote{ my @log; foreach my $i(1..$LAST_ID){ next if !@{$VT[$i]}; $VT[$i][18] = $VT[$i][7]; $VT[$i][19] = $VT[$i][8]; $VT[$i][20] = $VT[$i][0]; foreach my $j(0..8){ $VT[$i][$j] = 0; } push(@log,'$VT['.$i.'] = ['.(join(',',@{$VT[$i]}))."];\n"); } open(OUT,">vote_log.cgi"); print OUT @log; print OUT "\n1;\n"; close(OUT); &make_vote_ck('reset'); } sub make_vote_ck{ if($_[0] eq 'set'){ my @t =localtime($times + 86400); $VT_TIME = timelocal(0,0,5,$t[3],$t[4],$t[5]); $VT_RANK = $VT_RUI = $VT_RECO = $VT_RCRUI = $VT_COUNT = $VT_CTRUI = $VRK_CK = 1; } if($_[0] eq 'reset'){ my @m =localtime($times); $m[4] += 1; if($m[4] > 12){ $m[4] = 1; $m[5] += 1; } $VT_RESET = timelocal(0,0,2,1,$m[4],$m[5]); $VT_RANK = $VT_RECO = $VRK_RS = $VRK_CK = 1; } if($_[0] eq 'restore'){ $VT_RANK = $VT_RUI = $VT_RCRUI = $VT_RECO = $VT_COUNT = $VT_CTRUI = $VRK_CK = 1; } open(OUT,">vote_ck.cgi"); print OUT "\$VT_TIME = '".$VT_TIME. "';\n"; print OUT "\$VT_RESET = '".$VT_RESET. "';\n"; print OUT "\$VT_RANK = '".$VT_RANK. "';\n"; print OUT "\$VT_RUI = '".$VT_RUI. "';\n"; print OUT "\$VT_RECO = '".$VT_RECO. "';\n"; print OUT "\$VT_RCRUI = '".$VT_RCRUI. "';\n"; print OUT "\$VT_COUNT = '".$VT_COUNT. "';\n"; print OUT "\$VT_CTRUI = '".$VT_CTRUI. "';\n"; print OUT "\$VRK_CK = '".$VRK_CK. "';\n"; print OUT "\$VRK_RS = '".$VRK_RS. "';\n"; print OUT "\$LAST_ID = '".$LAST_ID. "';\n"; print OUT "\n1;\n"; close(OUT); }

    • ベストアンサー
    • Perl
  • close(STDOUT)するとCGIが終了する。

    Windows2000+Apache2.2+ActivePerlでCGIを作っています。 CGIで時間のかかる処理を行いたいので、以下のような処理を行いました。 print "Content-type: ~ ~ブラウザに返す文字列を標準出力へ~ close(STDOUT); ~時間のかかる処理~ ところが、「時間のかかる処理」の部分は実行されず、どうやらSTDOUTをクローズしたことにより、 ApacheによってCGIが終了させられてしまうようです。 ネットなどで見るかぎり、UNIX系のサーバーではよく行われる方法のようなのですが、Windowsでの実装になにか違いがあるのでしょうか? また、Windowsサーバーでこのようにブラウザに処理を戻しつつ、時間のかかる処理を行いたい場合にはどうすればよいのでしょうか?

    • ベストアンサー
    • CGI
  • Perlのforkについて

    PerlでDBに格納するプログラムを作成しています。 パラレルで行ないたいためにforkを使っているのですが、 いまいちわかっていないのでご教授ください。 やりたいことはパラレルでのDB格納です。 親、子両方のプロセスが完了したら後続処理を行い、 0を戻したいのですが、下記の方法ですと、 子が終了したらprintしてからreturnしているようです。 このように、親子の処理が完了したら後続処理に移るようにするためには どうしたらよいのでしょうか? if ($pid = fork()) { #親プロセス insertDB(); wait; return 0; } else { insertDB(); } print"ここは最後に通したい"\n;

    • ベストアンサー
    • Perl

専門家に質問してみよう