4 Stimmen

Wie kann ich eine Methode in einem laufenden Perl-Prozess von einem anderen Prozess aus aufrufen?

Ok, also, ich habe das Signal durch, aber aus irgendeinem Grund der Prozess existiert, nachdem es das Signal empfängt.

Wenn ich eine Endlosschleife (while(1) ) einfüge, bevor ich den Socket erstelle, funktioniert es wie vorgeschrieben. Also... irgendetwas in meinem Socket-Code bricht ab, wenn der Kill-Befehl ausgegeben wird.

Ich wüsste allerdings nicht, was das sein sollte. Ohne den Kill sitzt der Prozess unendlich lange da und nimmt Verbindungen an und sendet Nachrichten an die Clients. Warum sollte der Kill (und die damit verbundene Inkrementierung der Variable) den Socket dazu veranlassen, aus der Schleife auszusteigen und den Prozess zu beenden?

Der Socket-Code befindet sich unter...

(Erneut bearbeitet)

$SIGNAL = 0;
        sub sigHandler{ 
            #&logData("SIGNALED");
            $SIGNAL++ ;
        }
        $SIG{"USR1"}=\&sigHandler;

        # Create a new socket, on port 9999
        my $PORT = 9999;
        print ("opening connection on port $PORT");
        $lsn = new IO::Socket::INET(Listen => 1, 
                                    LocalPort => $PORT,
                                    Reuse => 1,
                                    Proto => 'tcp' );
           #or die ("Couldn't start server: $!");

        # Create an IO::Select handler
        $sel = new IO::Select( $lsn );

        # Close filehandles

        close(STDIN); close(STDOUT);

        warn "Server ready.  Waiting for connections . . .  on \n";

        # Enter into while loop, listening to the handles that are available.
        # this SHOULD be an infinite loop... I don't see why it would eval to false when 
        # I send a signal to increment $SIGNAL by one.
        while( @read_ready = $sel->can_read ) {
            $MESSAGE = 0;
            $fh  = $read_ready[0];

                # Create a new socket
                if($fh == $lsn) {
                    $new = $lsn->accept;
                    $sel->add($new);
                    push( @data, fileno($new) . " has joined.");
                    warn "Connection from " . $new->peerhost . ".\n";

                }

                # Handle connection
                else {

                    $input = <$fh>;
                    chomp $input;
                    warn "GOT INPUT '$input'\n";

                    if($input eq "<policy-file-request/>"){
                        $MESSAGE = 
                            qq~<?xml version="1.0"?>
                            <cross-domain-policy>
                              <allow-access-from domain="*" to-ports="*"/>
                            </cross-domain-policy>\0~;
                        $SIGNAL++;
                    }

                    if ( $input eq '') {#disconnection notification by client

                        warn "Disconnection from " . $new->peerhost . ".\n";                
                        $sel->remove($fh);
                        $fh->close;
                    }
                    if ( $input eq 'READY'){
                        warn "CLIENT READY = 1\n";
                        $CLIENT_READY = 1;
                    }

                }

            # Write to the clients that are available
            foreach $fh ( @write_ready = $sel->can_write(0) ) {
                if($MESSAGE == 0){
                    #set message here based on criteria
                    $MESSAGE = "UPDATE";
                }
                warn "outside send if\n";
                if($CLIENT_READY == 1 && $SIGNAL > 0){
                    warn ("sending $MESSAGE to $fh\n");         
                    $CLIENT_READY = 0;          
                    $SIGNAL--;
                    print $fh "$MESSAGE\0" or warn "can't send message to $fh";  
                }             
            }
        }

        warn "Server ended.\n";

6voto

rsp Punkte 22749

Wenn Sie eine Signalhandler in Ihrem Perl-Skript, muss es das Skript nicht beenden. (Beenden Sie den Handler nicht durch den Aufruf von die ) Beachten Sie, dass Sie weder SIGINT noch SIGKILL senden müssen, Sie können auch SIGUSR1 senden.

Editer In der Befehlszeile Ihres Kill-Befehls steht ein Komma hinter -USR1, das dort nicht stehen sollte (es lautet kill -USR1 4169)

Bearbeiten 2 Die while-Schleife (can_read) existiert wahrscheinlich, wenn sie ein leeres Array von Datei-Handles erhält, wenn sie durch das Signal unterbrochen wird. Sie könnten dies durch eine Bedingung verhindern:

while ((@read_ready = $sel->can_read) || 0 < $sel->count ) {

und aktualisieren Sie die Schleife, um eine leere @ready Array.

6voto

Noufal Ibrahim Punkte 68934

Ich weiß nicht, ob ich Ihre Frage falsch verstanden habe oder ob Sie das Offensichtliche übersehen haben. Ich würde mich nicht auf Signale verlassen, um so etwas zu tun.

Ich würde scriptA auf einen Socket hören und scriptB eine Nachricht senden an scriptA (und nicht ein Signal). Wenn er die richtige Nachricht empfängt, würde er die entsprechenden Daten an alle mit ihm verbundenen Clients weiterleiten.

3voto

aks Punkte 21959

Nur um die vorherigen Antworten/Kommentare zu ergänzen. Sie können einen Handler für SIGUSR1/2 schreiben, um externe Signale abzufangen und entsprechende Unterprogramme in Ihrem Skript auszuführen. Alles, was scriptB tun muss, ist "kill -SIGUSR[12] {pidof scriptA}" aufzurufen, um den Handler in scriptA aufzurufen.

Ein Beispiel für einen Signal-Handler wäre etwa so -

#!/usr/bin/perl
use strict;
use warnings;

$SIG{USR1} = sub { print "Caught USR1\n"; };
$SIG{USR2} = sub { print "Caught USR2\n"; };

while (sleep 5) {}

Vergewissern Sie sich, dass Sie keinen die/exit-Aufruf in Ihren Unterprogrammen zur Signalbehandlung haben, damit das Programm nach der Behandlung des Aufrufs ohne Unterbrechung fortgesetzt werden kann.

[EDIT] Nachdem ich mir die Frage noch einmal angesehen habe, denke ich, dass Noufals Anfahrt wäre eine bessere Option, da scriptA bereits einen Socket geöffnet hat. Lassen Sie doch scriptB mit scriptA über den Socket kommunizieren und leiten Sie den Kontrollfluss entsprechend Ihren Anforderungen um.

[EDIT2] Ich habe mir den zugrundeliegenden IO::Select-Code für can_read angesehen, und anscheinend gibt es einen Aufruf von select(), um die lesebereiten Dateideskriptoren zu erhalten. Da in Ihrem Fall der Timeout-Wert undef ist, blockiert der select-Aufruf. Laut dem Dokumentation bei perldoc ist der Neustart von select nach einem Signalhandler implementierungsabhängig. In Ihrem Fall (und in meinem) wird der select()-Aufruf "nicht" neu gestartet. Daher wird ein leeres Array an die while-Bedingungsprüfung zurückgegeben, was dazu führt, dass der Server beendet wird.

Ein schneller Hack zur Umgehung dieses Problems ist -

while ((@read_ready = $sel->can_read) || 1) {
    next if @read_ready == 0;
    ...
}

Das ist der Punkt, an dem ich in Betracht ziehen würde, alles zu verwerfen und den Designfluss zu überdenken.

2voto

Adam Punkte 406

Durch die Verwendung von Signalen könnten Sie einige subtile Fehler einführen, je nachdem, was scriptA tut, wenn das Signal übermittelt wird. Ich würde stattdessen empfehlen, dass Sie eine Prüfung in der scriptA Hauptschleife, die nach Kommunikation von scriptB .

Ich würde außerdem empfehlen, dass diese Kommunikation in Form einer benannten Semaphore erfolgt, die beide Skripte öffnen. scriptB kann die Semaphore bei Bedarf veröffentlichen, und scriptA kann eine nicht-blockierende $sem->trywait() an einem bestimmten Punkt der Schleife. Siehe POSIX::RT::Semaphore für weitere Informationen über die genaue Syntax.

Mit dieser Methode, scriptA muss entscheiden, wann es angemessen ist, die Anweisung von scriptB und Sie vermeiden alle unangenehmen Probleme, die durch die Unterbrechung von Unterprogrammen durch asynchrone Signalübertragung entstehen können.

Weitere Signale und deren Feinheiten finden Sie in der Perl-Kochbuch et Perl programmieren .

1voto

Adam Flott Punkte 487

Auch wenn dies vielleicht nicht zu Ihren Anforderungen gehört, könnten Sie, wenn Sie mehrere Methoden aufrufen möchten, eine RPC-Schnittstelle (Remote Procedure Call) einrichten. Etwas wie Ereignis::RPC aber das könnte zu viel des Guten sein.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X