2016-05-17 17 views
1

Ich gehe durch ein Perl-Skript, das waitpid($pid, 0) verwendet, um auf den aktuellen Prozess zu warten. Aber print Anweisung direkt danach geschrieben waitpid wird gedruckt, bevor der Prozess abgeschlossen ist.Wie warten, bis der Prozess in Perl ausgeführt wird, wenn der Prozess nicht untergeordnet ist?

Ich möchte wissen, warum waitpid nicht darauf wartet, dass der Prozess zuerst abgeschlossen wird.

Auch die Steuerung des laufenden Prozesses ist unter anderen Modul, nicht Teil dieses Perl-Skript. Nur zugänglich ist pid und Name des Prozesses. Ich kann nichts in dem Modul ändern, das den Prozess aufruft.

+0

Sind Sie sicher, dass $ pid das ist, was Sie denken, dass es ist? Was ist der Rückgabewert von 'waitpid'? Es zeigt an, welcher Prozess beendet wurde. – Schwern

+0

@Schwern - Rückgabewert von waitpid ist -1. Dies bedeutet, dass es keinen solchen Kindprozess gibt, denke ich. Und ich habe unten Zeilen hinzugefügt, um zu überprüfen, ob $ pid läuft oder nicht. mein $ exists = kill 0, $ pid; print "Prozess läuft \ n" if ($ exists); Und es druckt die Aussage – user3395103

+2

Sie können nicht. Sie können nur auf einen Child-Prozess warten. Siehe 'perldoc wait' (oder' waitpid', es ist das gleiche), erster Satz. Wenn Sie wissen wollen, wann ein externes Programm abgeschlossen ist, müssen Sie ganz andere Dinge tun. – zdim

Antwort

2

Hinweis   Ein einfältiger Einzeiler mit kill 0, $pid ist am Ende, kommentiert.


Wir müssen den Abschluss eines externen Programms erkennen, das von diesem Skript nicht gestartet wurde. Die Frage fragt nach der Verwendung von waitpid. Um meinen frühen Kommentar zu kopieren:

Sie können nicht. Sie können nur auf einen Kindprozess warten. Siehe perldoc wait (oder waitpid, es ist das gleiche), erster Satz.

Die wait und waitpid Warten auf Signale an das Skript über das Schicksal seines Kind geboren (ern). Es gibt keinen Grund dafür, dass das Skript solche Signale über Prozesse empfängt, die nicht gestartet wurden.


Wir kennen die ID des Prozesses und seinen Namen.Seine PID kann verwendet werden, um Abfrage für, ob es ausgeführt wird. Die Verwendung von pid alleine ist nicht vollständig zuverlässig, da zwischen unseren Prüfungen der Prozess beendet werden kann und ein zufälliger neuer derselben pid zugewiesen werden kann. Wir können den Namen des Programms verwenden, um dies zu verstärken.

Auf einem Linux-System können Informationen über einen Prozess abgerufen werden, indem (viele) ps Optionen verwendet werden. Jede dieser kehrt das Programm zum vollständigen Aufruf

 
ps --no-headers -o cmd PID 
ps --no-headers -p PID -o cmd 

Der zurückgegebene String kann mit dem Dolmetscher Pfad starten (für ein Perl-Skript, zum Beispiel), gefolgt von dem vollständigen Namen des Programms. Die Version ps -p PID -o comm= gibt nur den Namen des Programms zurück, aber ich finde, dass es dieses Wort an einem Bindestrich (wenn dort) brechen kann, was zu einem unvollständigen Namen führt. Dies kann bei einigen Systemen eine Feinabstimmung erfordern, konsultieren Sie bitte Ihre . Wenn es keinen Prozess mit gegebener PID gibt, bekommen wir nichts zurück.

Dann können wir auf PID überprüfen und wenn gefunden, überprüfen, ob der Name für diese PID mit dem Programm übereinstimmt. Der Name des Programms ist bekannt und wir könnten das nur hart codieren. Es wird jedoch immer noch vom Skript beim Starten unter Verwendung des obigen Befehls ps erhalten, um Mehrdeutigkeiten zu vermeiden. (Dann ist es auch im gleichen Format für späteren Vergleich.) Dies selbst wird gegen den bekannten Namen überprüft, da es keine Garantie gibt, dass die PID zum Zeitpunkt der Skriptausführung tatsächlich für das erwartete Programm ist.

use warnings; 
use strict; 

# For testing. Retrieve your PID as appropriate for real use  
my $ext_pid = $ARGV[0] || $$; 

my $cmd_get_name = "ps --no-headers -o cmd $ext_pid"; 

# For testing. Replace 'sleep' by your program name for real use 
my $known_prog_name = 'sleep'; 

# Get the name of the program with PID 
my $prog_name = qx($cmd_get_name); 

# Test against the known name, exit if there is a mismatch 
if ($prog_name !~ $known_prog_name) { 
    warn "Mismatch between:\n$prog_name\n$known_prog_name -- $!"; 
    exit; 
} 

my $name; 
while ($name = qx($cmd_get_name) and $name =~ /$prog_name/) 
{ 
    print "Sleeping 1 sec ... \n"; 
    sleep 1; 
} 
# regex above may need slight adjustment, depending on format of ps return 

Die Befehlsausgabe, empfangen über qx() oben (Graviszeichen Operator) eine neue Zeile enthält. Wenn sich herausstellt, dass das Skript ein Problem darstellt, kann es chomp -ed sein, was eine leichte Anpassung erfordern würde. Die verbleibende Lücke ist, dass , dass sehr Programm möglicherweise beendet wurde und wurde zwischen den Prüfungen und mit der gleichen PID neu gestartet.

Dies würde durch Laufen in einer Schale

 
sleep 30 & 
script.pl `ps aux | egrep '[s]leep'` 

Die egrepgrep -E ist getestet werden. Die Ausgabe von `ps ...` enthält mehrere Wörter. Diese werden als Befehlszeilenargumente an unser Skript übergeben, das die erste als PID verwendet. Wenn es ein Problem damit gibt, führen Sie zuerst die Filterung ps durch und geben Sie dann die PID als Eingabeargument des Skripts manuell ein. Die sleep von 30 Sekunden oben soll genug Zeit geben, das alles auf der Kommandozeile zu tun.

Der Code kann vereinfacht werden, indem $name mit einem hartcodierten $prog_name verglichen wird, wenn der Programmname eindeutig genug ist und sich nicht ändert. Der hartcodierte Name ist oben verwendet, aber für eine Überprüfung und es generiert eine Warnung, wenn nicht übereinstimmte. (Wenn wir auf sie verlassen nur hartkodierte können wir Warnungen nicht ausgeben, wenn es nicht übereinstimmt, denn das ist dann ein Teil des Codes der Operation.)


Wenn der Prozess durch den gleichen Benutzer wie das Skript gehört ein kill 0, $pid verwenden können als

while (kill 0, $ext_pid) { sleep 1 } 

Dann würden Sie entweder einen weiteren Anruf zu machen, den Namen zu überprüfen oder mit dem (kleinen) Möglichkeit eines Fehlers zufrieden sein, in welchem ​​tatsächlichen Prozess der $pid darstellt.

Das Modul Proc::ProcessTable kann stattdessen für alle diese verwendet werden. Auf einem Windows-System wäre dies der richtige Weg.

+0

ja, es funktionierte für mich – user3395103

+0

@ user3395103 Froh, es hat funktioniert. Es hat sich ein langer Post ergeben, lassen Sie mich wissen, wenn es weitere Fragen oder Probleme gibt. – zdim

3

Die waitpid documentation Zustände:

Waits für einen bestimmten Kind Prozess zu beenden und gibt die pid des Verstorbenen Prozess, oder -1, wenn es kein solches Kind-Prozess ist.

Schnelltest:

my $pid = open my $fh,"-|","sleep 3"; 
print waitpid(28779,0); # Some other process 
print waitpid($pid,0); 

28779 ist ein anderer Prozess derzeit läuft (nahm eine gelegentliche von ps axu). Ausgang:

-1 
4088 

Sie können nicht waitpid verwenden für einen Prozess zu warten, die kein Kind des aktuellen Prozesses.

The kill command überprüfen können, wenn ein PID zur Zeit läuft:

print kill(0,28779); 

Ausgang:

1 

Sie noch abfragen, bräuchten bis (Schleife mit Schlaf) für die pid zu verschwinden. Denken Sie auch daran, dass der überwachte Prozess möglicherweise beendet wird und ein neuer möglicherweise die gleiche PID vor der nächsten Überprüfung wieder verwendet (unwahrscheinlich, aber möglich).

Verwandte Themen