2011-01-17 12 views
13

Ich habe ein Perl-Skript, das Gabeln.Was passiert mit einem SIGINT (^ C), wenn es an ein Perl-Skript gesendet wird, das Kinder enthält?

Jeder Fork führt ein externes Programm aus, analysiert die Ausgabe und konvertiert die Ausgabe in eine speicherbare Datei.

Die speicherbaren Dateien werden dann vom übergeordneten System eingelesen und die Gesamtdaten von jedem der untergeordneten Elemente werden analysiert, bevor eine Wiederholung der vorherigen Verzweigung ausgeführt wird oder andernfalls die übergeordnete Datei angehalten wird.

Was genau passiert, wenn ich ein^C ausstelle, während einige der Kinder noch das externe Programm laufen haben? Das Parent-Perl-Skript wurde im Vordergrund aufgerufen und ich vermute, trotz Forking blieb es im Vordergrund.

Wird das SIGINT an alle Kinder übergeben, dh an die Eltern, die Kinder der Eltern und an das von den Kindern aufgerufene externe Programm?

UPDATE:

ich hinzufügen soll, scheint es, dass, wenn ich die SIGINIT ausgeben, das externe Programm mit den Kindern meines Skripts aufgerufen scheint das Signal zu erkennen und zu beenden. Aber die Kinder, oder vielleicht das Elternprogramm, machen weiter. Das ist alles unklar für mich.

UPDATE 2:

Hinsichtlich tchrist Kommentar wird das externe Programm mit system() Perl-Befehl aufgerufen.

Tatsächlich scheint tchrists Kommentar auch die Erklärung zu enthalten, nach der ich gesucht habe. Nach etwas mehr Debugging, basierend auf dem Verhalten meines Programms, scheint es, dass SIGINT tatsächlich vom Elternteil an alle Kinder und von allen Kindern an alle ihre Kinder (das externe Programm) weitergegeben wird.

Also, was auf der Grundlage des Kommentars von tchrist zu geschehen scheint, ist, dass CTRL-C das externe Programm auslöst, das bewirkt, dass die Kinder aus dem Befehl system() auswandern - und nichts mehr.

Obwohl ich meine Kinder überprüfen den Exit-Status von dem, was in system() aufgerufen wurde, ging ich davon aus, dass ein STRG-C alles von der übergeordneten abtöten würde, anstatt zur Schaffung von mehr Runden der Verarbeitung führen, was ist Was ist passiert!!!

LÖSUNG (auf mein Problem):

Ich muss nur ein Signal-Handler für SIGINT in der übergeordneten erstellen. Der Signal-Handler würde dann SIGTERM an jeden der untergeordneten Objekte senden (von denen ich anmaße, dass sie auch ein SIGTERM an die untergeordneten Objekte der Kinder senden würden) und dann veranlassen, dass das übergeordnete Element ordnungsgemäß beendet wird. Obwohl diese etwas offensichtliche Lösung wahrscheinlich Dinge behoben hätte, wollte ich meine falsche Auffassung über das Verhalten von SIGINT in Bezug auf das Forking in Perl verstehen.

+2

Sie haben nicht gesagt, ob Sie Ihre eigene Version von 'system()' oder eine Pipe öffnen oder Backticks, oder ob Sie Perls verwenden. Wenn Sie Perls verwenden, IGNORIERT jedes Elternteil - dasjenige, das "System" nennt, nicht das von ihm aufgerufene - SIGINT und SIGQUIT, während die Kinder laufen. Wenn Sie Ihre eigenen Rollen haben, müssen Sie selbst über diese Dinge nachdenken. Überlegen Sie, was passiert, wenn Sie 'system (" vi somefile ")' verwenden und während einer langen Suche in 'vi' auf^C drücken: nur' vi' nimmt einen (nicht-tödlichen) SIGINT; Der Elternteil ignoriert es. Das ist richtiges Verhalten. – tchrist

+0

Danke, tchrist. Mache dies zu einer Antwort, damit ich es als akzeptiert markieren kann. – EMiller

Antwort

12

Perls eingebaute system Funktion funktioniert genau wie das C System (3) Funktion von der Standard-C-Bibliothek, soweit Signale betroffen sind. Wenn Sie die Perl-Version system() oder Pipe Open oder Backticks verwenden, IGNORIERT jedes Elternteil - dasjenige, das system aufruft, anstatt das von ihm aufgerufene - SIGINT und SIGQUIT, während die Kinder laufen. Wenn du dich selbst mit einer Variante des fork?wait:exec Trios gedreht hast, dann musst du selbst über diese Dinge nachdenken.

Überlegen Sie, was passiert, wenn Sie system("vi somefile") verwenden und während einer langen Suche in vi^C drücken: nur vi braucht einen (nicht-tödlichen) SIGINT; Der Elternteil ignoriert es. Das ist richtiges Verhalten. Deshalb funktioniert C so und deshalb arbeitet Perl so.

Das, was Sie sich erinnern müssen, ist, dass, nur weil ein^C sendet ein SIGINT an alle Prozesse im Vordergrund Prozessgruppe (auch die der effektiven UID oder GID abweichend), das nicht bedeuten, dass es alle, die bewirkt, dass Prozesse zum Beenden. A^C ist nur ein SIGINT, gemeint ist ein Interrupt ein Prozess, kein SIGKILL, der ohne Fragen enden soll.

Es gibt viele Arten von Programmen, die es falsch wäre, ohne Warnung zu töten; Ein Editor ist nur ein Beispiel dafür. Ein Mailer könnte ein anderer sein. Sei dabei äußerst vorsichtig.

Viele Arten von Programmen ignorieren, fangen oder blockieren (bedeutet verzögerte Lieferung) verschiedener Arten von Signalen. Nur das Standardverhalten von SIGINT bewirkt, dass der Prozess beendet wird.Sie können herausfinden, ob dies geschehen ist, und in der Tat des Signal verursachte es (unter anderem) passieren, mit dieser Art von Code auf herkömmlichen Betriebssystemen:

if ($wait_status = system("whatever")) { 
    $sig_killed = $wait_status & 127; 
    $did_coredump = $wait_status & 128; 
    $exit_status = $wait_status >> 8; 
    # now do something based on that... 
} 

Hinweis sorgfältig, dass a^C'D vi, zum Beispiel wird nicht ein Wartestatuswort haben, das anzeigt, dass es von einem ungepackten SIGINT gestorben ist, da es keinen gab: es fing es ein.

Manchmal werden Ihre Kinder gehen und Kinder hinter dem Rücken haben. Unordentlich, aber wahr. Ich habe deshalb bekannt, gelegentlich zu Völkermord alle Nachkommen bekannten und unbekannten auf diese Weise:

# scope to temporize (save+restore) any previous value of $SIG{HUP} 
{ 
    local $SIG{HUP} = "IGNORE"; 
    kill HUP => -$$; # the killpg(getpid(), SIGHUP) syscall 
} 

Das ist natürlich nicht mit SIGKILL oder SIGSTOP funktioniert, die nicht zugänglich sind werden so nicht berücksichtigt.

Eine andere Sache, die Sie vorsichtig sein sollten, ist, dass vor der Version 5.8 die Signalverarbeitung in Perl in der Vergangenheit keine zuverlässig sichere Operation war. Es ist jetzt, aber das ist ein versionsabhängiges Problem. Wenn Sie das noch nicht getan haben, sollten Sie sich unbedingt auf deferred signals in the perlipc manpage und vielleicht auch auf die PERL_SIGNALS envariable in the perlrun manpage nachlesen.

2

Wenn Sie den übergeordneten Prozess beenden, werden die untergeordneten (und externen Programme) weiterhin ausgeführt, bis sie auf die eine oder andere Weise beendet werden. Dies kann vermieden werden, wenn der Elternteil einen Signalhandler hat, der das SIGINT abfängt, und dann die Prozessgruppe (oft die PID des Elternteils) beendet. Sobald der Elternteil das SIGINT bekommt, werden alle Kinder getötet.

Um Ihre Frage zu beantworten, hängt alles von der Implementierung des Signalhandlers ab.Basierend auf Ihrem Update scheint es, dass der Elternteil tatsächlich seine Kinder tötet und dann, anstatt sich selbst zu beenden, geht es zurück, etwas anderes zu tun (denken Sie an es als ein Zurücksetzen anstelle einer kompletten Abschalt-/Start-Kombination).

+0

In diesem das Standardverhalten? Ich habe selbst keine Signalhandler in mein Programm eingefügt. Ich würde denken, dass eine Kontrolle C alles töten würde und ich bin schockiert, dass dies nicht der Fall ist. – EMiller

+0

Standardverhalten für SIGINT ist zu beenden. Aber da Ihr Elternteil das nicht tut und Sie keine Handler haben oder die IDs manuell geändert haben, passiert etwas anderes. Kann es sein, dass sich die externen Programme auch verzweigen? – Milan

8

Wenn Sie^C in einem Terminalfenster (oder irgendeinem anderen Terminal) drücken, sendet es ein SIGINT an die Vordergrundprozessgruppe in diesem Terminal. Wenn Sie nun ein Programm über die Befehlszeile starten, wird es im Allgemeinen in einer eigenen Prozessgruppe angezeigt, und das wird zur Vordergrundprozessgruppe. Wenn Sie ein untergeordnetes Element auszweigen, befindet es sich standardmäßig in der gleichen Prozessgruppe wie das übergeordnete Element. Daher werden standardmäßig das übergeordnete Element (Programm der obersten Ebene, das über die Befehlszeile aufgerufen wird), alle untergeordneten Elemente, untergeordneten Elemente usw. sowie Alle externen Programme, die von diesen aufgerufen werden (die alle nur untergeordnete sind), befinden sich alle in derselben Prozessgruppe, so dass alle das Signal SIGINT empfangen.

Wenn jedoch eines dieser Kinder oder Programme setpgrp oder setpgid oder sedid oder einen anderen Aufruf aufrufen, der den Prozess in einer neuen Fortschrittsgruppe verursacht, werden diese Prozesse (und alle untergeordneten Prozesse nach dem Verlassen des Vordergrundprozesses gestartet Gruppe) erhält das SIGINT NICHT.

Wenn ein Prozess ein SIGINT empfängt, wird es möglicherweise nicht beendet - es könnte das Signal ignorieren oder es könnte es fangen und etwas völlig anderes tun. Der Standard-Signal-Handler für SIGINT beendet den Prozess, aber das ist nur der Standardwert, der überschrieben werden kann.

bearbeiten

Von Ihrem Update, es klingt wie alles in der gleichen Prozessgruppe verbleibt, so dass die Signale an alle werden geliefert, wie die Enkel (die externen Programme) sind Verlassen, aber die Kinder fangen und ignorieren die SIGINTs. Nach dem Kommentar von tchrist klingt das nach dem Standardverhalten von Perl.