2017-01-13 7 views
1

Tclスクリプトでプロセスが実行されたときに、fork()が実行され、フォークされたプロセスが実行されたままになります。実行後にバックグラウンドで実行するように設定されていれば、例えばgvimのように、バックグラウンドにフォークするプログラムを実行するだけで、試してみることができます:set res [exec gvim]プロセスがフォークして終了すると、[exec]プロセスはゾンビを去ります

メインプロセスは理論的にすぐに終了しますが、子プロセスはバックグラウンドで実行されますが、何らかの理由でメインプロセスがハングアップし、終了せず、ゾンビ状態になります(<defunct>ps出力)。

私の場合、印刷を開始しているプロセスがあります。そのプロセスが終了するようにしたいと思います。問題は、私がopen "|gvim" rを使ってプロセスを起動した場合、プロセスが終了した瞬間も認識できないということです。 fd[open]によって返された場合、プログラムがゾンビになっても、[eof]は報告されません。 [read]を試すと、プロセスが印刷する可能性があるすべてのものを読み込むだけで、完全にハングアップします。

さらに興味深いことに、メインプロセスとフォークされたプロセスの両方で印刷されることがあり、[gets]を使用して読み取ろうとしているときに両方が表示されることがあります。ディスクリプタをあまりに早く閉じると、パイプが壊れて[close]が例外をスローします。おそらく、[read]が終わることはないでしょう。

メインのプロセスが終了した瞬間を認識するための方法が必要ですが、このプロセスによって別の子プロセスが生成される可能性がありますが、この子プロセスは完全に切り離されている可能性があります。私は、メインプロセスが終了する前に印刷して、スクリプトがその作業を続行し、バックグラウンドで実行中のプロセスも実行されていて、何が起こるのか興味がないものを欲しいです。

私は始めているプロセスのソースを管理しています。はい、私はsignal(SIGCLD, SIG_IGN)の前にfork()の前に助けませんでした。

答えて

0

[OK]を、私はここで長い議論の後に解決策を見つけた:

#!/usr/bin/tclsh 

lassign [chan pipe] input output 
chan configure $input -blocking no -buffering line ;# just for a case :) 

puts "Running $argv..." 
set ret [exec {*}$argv 2>@stderr >@$output] 
puts "Waiting for finished process..." 
set line [gets $input] 
puts "FIRST LINE: $line" 
puts "DONE. PROCESSES:" 
puts [exec ps -ef | grep [lindex $argv 0]] 
puts "EXITING." 

:以下のスクリプトは、この問題を解決することができる方法を示し

https://groups.google.com/forum/#!topic/comp.lang.tcl/rtaTOC95NJ0

残っている唯一の問題は、まだ可能性がないということですプロセスが終了したことを知るために、次の[exec](この特定のケースではおそらく[exec ps...]コマンドがこれを実行しました)がゾンビをクリーンアップします(これは普遍的な方法ではありません - POSIXシステムでできることは、[exec /bin/true]です)。私の場合は、親プロセスが印刷しなければならなかった行が1行であれば十分でした。その後、単純に「放棄」することができます。

[exec]は、最初のプロセスのPIDを返すことができ、プロセスが終了するか、実行状態をチェックするまでブロックすることができる標準[wait]コマンドがあります(このコマンドは現在TclXで利用可能です)。

[chan pipe]はTcl 8.6でのみ利用可能であり、TclXからは[pipe]を代わりに使用できることに注意してください。

0

デーモンは、setsid()setpgrp()を呼び出して、新しいセッションを開始し、プロセスグループからデタッチすることもできます。しかし、これらはあなたの問題にも役立ちません。

あなたは、いくつかのプロセス管理を行う必要があります。

#!/usr/bin/tclsh 

proc waitpid {pid} { 
    set rc [catch {exec -- kill -0 $pid}] 
    while { $rc == 0 } { 
    set ::waitflag 0 
    after 100 [list set ::waitflag 1] 
    vwait ::waitflag 
    set rc [catch {exec -- kill -0 $pid}] 
    } 
} 

set pid [exec ./t1 &] 
waitpid $pid 
puts "exit tcl" 
exit 

編集:別の不合理な答え

フォークした子プロセスがオープンチャンネルを閉じた場合、Tclはそれを待つことはありません。

テストプログラム:

#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <signal.h> 

int 
main (int argc, char *argv []) 
{ 
    int pid; 
    FILE *o; 

    signal (SIGCHLD, SIG_IGN); 
    pid = fork(); 
    if (pid == 0) { 
    /* should also call setsid() and setpgrp() to daemonize */ 
    printf ("child\n"); 
    fclose (stdout); 
    fclose (stderr); 
    sleep (10); 
    o = fopen ("/dev/tty", "w"); 
    fprintf (o, "child exit\n"); 
    fclose (o); 
    } else { 
    printf ("parent\n"); 
    sleep (2); 
    } 
    printf ("t1 exit %d\n", pid); 
    return 0; 
} 

テストのTclプログラム:

#!/usr/bin/tclsh 

puts [exec ./t1] 
puts "exit tcl" 
0

Tclはそれがexecを呼び出し時間を呼び出して、バックグラウンド・プロセスからゾンビをクリア。ゾンビは実際には多くのリソースを使用しないので(プロセステーブルのエントリですが、そこには他に何もありません)、それらをクリーンアップする特別な急いではありません。


パイプラインで問題が発生したということは、非ブロックモードにしないことでした。パイプラインの終了を検出するには、を使用することをお勧めします。のいずれかがの場合、パイプの一端が閉じられると、パイプまたはのいずれかが読み取られます。これらのケースを区別するためには、実際に読み込もうとする必要があります。読み書きをしていて、非ブロックモードでない場合はブロックできます。しかし、Tclはノンブロッキングモードでの作業を容易にします。それは、ユーザーが対話アプリケーションであるよう

set pipeline [open |… "r"] 
fileevent $pipeline readable [list handlePipeReadable $pipeline] 
fconfigure $pipeline -blocking false 

proc handlePipeReadable {pipe} { 
    if {[gets $pipe line] >= 0} { 
     # Managed to actually read a line; stored in $line now 
    } elseif {[eof $pipe]} { 
     # Pipeline was closed; get exit code, etc. 
     if {[catch {close $pipe} msg opt]} { 
      set exitinfo [dict get $opt -errorcode] 
     } else { 
      # Successful termination 
      set exitinfo "" 
     } 
     # Stop the waiting in [vwait], below 
     set ::donepipe $pipeline 
    } else { 
     # Partial read; things will be properly buffered up for now... 
    } 
} 

vwait ::donepipe 

は、通常よりもむしろ、より複雑な...パイプラインで gvimを使用することがあることに注意してください。あなたはそれが簡単に別のスレッドでシンプルな execを実行するために見つけるかもしれない


は、スレッド対応であるとThreadパッケージがインストールされているのTclのバージョンを提供します。 (それはあなたが8.6を使用している場合場合であるべき、しかしそれが本当ならば、私は知りません。)すべてのことについては

package require Thread 

set runner [thread::create { 
    proc run {caller targetVariable args} { 
     set res [catch { 
      exec {*}$args 
     } msg opt] 
     set callback [list set $targetVariable [list $res $msg $opt]] 
     thread::send -async $caller $callback 
    } 
    thread::wait 
}] 

proc runInBackground {completionVariable args} { 
    global runner 
    thread::send -async $runner [list run [thread::id] $completionVariable {*}$args] 
} 

runInBackground resultsVar gvim … 
# You can do other things at this point 

# Wait until the variable is set (by callback); alternatively, use a variable trace  
vwait resultsVar 

# Process the results to extract the sense 
lassign $resultsVar res msg opt 
puts "code: $res" 
puts "output: $msg" 
puts "status dictionary: $opt" 

は、gvimのようなエディタのために私は実際はそれを期待しますそれらのうちの1つだけが特定の端末と実際に一度に対話することができるので、前景(これはあまり複雑でないものは必要ありません)で実行されます。まず

+0

私は何とかあなたが合理的に答える唯一の人であることを知っていました:)。残念なことに、あなたの解決策のどれも働いていません。最初のものが 'gets'でハングアップします。スレッドの' exec'でハングアップします。これは 'gvim'でも簡単に証明できます(私は簡単にテストできるようにしました)。私は解決策が必要です:ソケット接続を受け取り、コマンドを取得し、bgでプロセスを実行し、忘れてしまいます。もう1つの要求がそれを殺すかもしれませんが、プロセスはcmdline argsによって認識されます。私はまた、プロセス自体をフォークする必要はありません、私はちょうどそれが簡単だろうと思った。 'exec ...&' 'slongsもやってもいいです。私は終了して実行し続けることができます – Ethouris

+0

このトピックに少し興味があればいいと思います。この問題は 'bash'には存在しません。バックグラウンドにフォークして実行中のプロセスを実行すると、CLIが復帰します。 Tclで同じことをやっても、 '[exec]'コマンドは戻ってこない(終了したプロセスはゾンビのようにぶら下がっています)。それは本当にデザインによって機能しますか?通常、ゾンビは、親によってクリーンアップされるまで終了したCHILDプロセスです。しかし、どうにかして何もフォークしない同じCHILDプロセスがゾンビなしで終了します。あなたはそれを説明できますか? – Ethouris

0

あなたが言う:

このプロセスは、別の子プロセスを生み出している可能性が、この子プロセスを完全に取り外すことも、私は」ながら、私は、ときメインプロセスが終了する瞬間を認識するために、いくつかの方法が必要それは何か興味がない。後であなたに

は言う:

フォークした子プロセスがオープンチャンネルを閉じた場合、Tclはそれを待つことはありません。

これは2つの矛盾した記述です。一方の手は親プロセスだけに興味があり、もう一方は子プロセスが終了したかどうかにかかわらず、分離した子プロセスには関心がないと言っています。最後に私はフォークを聞いて、親のstdin、stdout、およびstderrの子のコピーをクローズしています(つまり、子プロセスをデーモン化しています)。上記のシンプルなCプログラムを実行するためにこのクイックプログラムを書いたところ、期待どおりのtclは子プロセスの何も知らない。私はコンパイルされたバージョンのプログラム/ tmp/compile/chuckを呼び出しました。私はgvimを持っていないのでemacsを使いましたが、emacsはテキストを生成しないので、私は自身のtclスクリプトとexecでexecをラップします。どちらの場合も、親プロセスが待機され、eofが検出されます。親がRunner :: getDataを終了すると、クリーンアップが評価されます。


#!/bin/sh 
exec /opt/usr8.6.3/bin/tclsh8.6 "$0" ${1+"[email protected]"} 

namespace eval Runner { 
    variable close 
    variable watch 
    variable lastpid "" 
    array set close {} 
    array set watch {} 


    proc run { program { message "" } } { 
     variable watch 
     variable close 
     variable lastpid 
     if { $message ne "" } { 
      set fname "/tmp/[lindex $program 0 ]-[pid].tcl" 
      set out [ open $fname "w" ] 
      puts $out "#![info nameofexecutable]" 
      puts $out " catch { exec $program } err " 
      puts $out "puts \"\$err\n$message\"" 
      close $out 
      file attributes $fname -permissions 00777 
      set fd [ open "|$fname " "r" ] 
      set close([pid $fd]) "file delete -force $fname " 
     } else { 
      set fd [ open "|$program" "r" ] 
      set close([pid $fd]) "puts \"cleanup\"" 
     } 
     fconfigure $fd -blocking 0 -buffering none 
     fileevent $fd readable [ list Runner::getData [ pid $fd ] $fd ] 
    } 

    proc getData { pid chan } { 
     variable watch 
     variable close 
     variable lastpid 
     set data [read $chan] 
     append watch($pid) "$data" 
     if {[eof $chan]} { 
      catch { close $chan } 
      eval $close($pid) ; # cleanup 
      set lastpid $pid 
     } 
    } 
} 
Runner::run /tmp/compile/chuck "" 
Runner::run emacs " Emacs complete" 

while { 1 } { 
    vwait Runner::lastpid 
    set p $Runner::lastpid 
    catch { exec ps -ef | grep chuck } output 
    puts "program with pid $p just ended" 
    puts "$Runner::watch($p)" 
    puts " processes that match chuck " 
    puts "$output" 
} 

出力: ノート子供はそれが出たことを報告した後、私は、Emacsの外に出ました。

[[email protected] workspace]$ ./test.tcl 
cleanup 
program with pid 27667 just ended 
child 
parent 
t1 exit 27670 
    processes that match chuck avahi  936  1 0 2016 ? 
    00:04:35 avahi-daemon: running [linuxrocks.local] admin 27992  1 0 
    19:37 pts/0 00:00:00 /tmp/compile/chuck admin 28006 27988 0 
    19:37 pts/0 00:00:00 grep chuck 

child exit 
program with pid 27669 just ended 

    Emacs complete 
+0

Tclスクリプトの自動生成?さて、私はむしろ '' chan pipe ''(または8.6のTclXからのパイプ)を試してみたいと思います。 – Ethouris

関連する問題