FrankS: Killt ein Klick auf Stopp-Button ein CGI-Script?

Hallo allezusammen!

Eine Frage beschäftigt mich zurzeit:
Wird ein CGI-Script vom Server gekillt, wenn während der Laufzeit des Scripts der User auf den Stopp-Button im Browser drückt?

Danke für Eure Antworten,
Gruß Frank

  1. Hi!

    Natürlich wird es nicht gestoppt, da das Script wie Du schon selbst richtig gesagt hast auf dem Server abläuft nicht auf dem User-Rechner.

    Das heisst, der User kann nur die Ansicht / Anwendung auf seinem Rechner beenden durch den Stopp-Button, nicht aber auf dem Server.

    Gruss,
    CS

    1. Hi!

      Auch Hi!

      Natürlich wird es nicht gestoppt, da das Script wie Du schon selbst richtig gesagt hast auf dem Server abläuft nicht auf dem User-Rechner.

      Ich bin mir nur nicht sicher, ob der Browser dem Server nicht "Bescheid sagt" - nach dem Motto: vergiß den Request, und tschüs. Schließlich liefert das Script am Ende eine Ausgabe, die zum Browser geschickt werden soll(te). Der Browser erwartet nun aber nichts mehr. D.h., daß sonst übliche Spiel http-request <-> http-response wird unterbrochen. Nur wo?

      Gruß Frank

      1. Moin.

        Ich bin mir nur nicht sicher, ob der Browser dem Server nicht "Bescheid sagt" - nach dem Motto: vergiß den Request, und tschüs. Schließlich liefert das Script am Ende eine Ausgabe, die zum Browser geschickt werden soll(te). Der Browser erwartet nun aber nichts mehr. D.h., daß sonst übliche Spiel http-request <-> http-response wird unterbrochen. Nur wo?

        Wenn du es nicht weißt, dann mach den Pepsi-Test. ;)

        Schreibe ein kleines Script, welches alle halbe Sekunde eine Zahl ausgibt, sowohl zum Browser, als auch in eine Logdatei. Zählen von 1 bis 100.

        Wenn das Skript nicht abgebrochen wird, müßten alle Zahlen in der Logdatei stehen. Wenn nicht, wird irgendwo mittendrin abgebrochen.

        - Sven Rautenberg

        1. Auch Moin!

          Wenn das Skript nicht abgebrochen wird, müßten alle Zahlen in der Logdatei stehen. Wenn nicht, wird irgendwo mittendrin abgebrochen.

          Damit kann er zwar seine spezielle Konfiguration testen, aber man kann leider keine allgemeinen Rueckschluesse ziehen. Und ein CGI-Script sollte ja schon auch mit verschiedenen Webservern laufen, oder?

          So long

          1. Nochmal Moin.

            Damit kann er zwar seine spezielle Konfiguration testen, aber man kann leider keine allgemeinen Rueckschluesse ziehen. Und ein CGI-Script sollte ja schon auch mit verschiedenen Webservern laufen, oder?

            Was spricht dagegen, die eigene Konfiguration zu testen?

            Abgesehen davon, daß ich bezweifle, daß man das durch Konfigurieren hinkriegen könnte: Falls ja, möchte man ja irgendwie wissen, ob die geänderte Konfiguration auch funktioniert, damit man weiß, was man anderswo evtl. ändern muß.

            - Sven Rautenberg

            1. Re!

              Was spricht dagegen, die eigene Konfiguration zu testen?

              Na nichts, sagt ja auch keiner. Man darf dann nur nicht sagen, bei mir funktioniert es so, also *ist das so*.

              Abgesehen davon, daß ich bezweifle, daß man das durch Konfigurieren hinkriegen könnte: Falls ja, möchte man ja irgendwie wissen, ob die geänderte Konfiguration auch funktioniert, damit man weiß, was man anderswo evtl. ändern muß.

              Konfiguration meinte ich hier in einem etwas globalerem Sinne, also ungefaehr "verwendete Software".

              So long

    2. Hoi,

      Natürlich wird es nicht gestoppt,

      Das ist leider falsch. Dem Script wird erst ein SIGTERM und dann ein
      SIGKILL geschickt.

      da das Script wie Du schon selbst richtig gesagt hast auf dem
      Server abläuft nicht auf dem User-Rechner.

      Das hat damit nichts zu tun. Wenn du den Request per Stop-Button oder
      per Escape abbrichst, wird die Verbindung zum Server gekillt. Das merkt
      der Apache und er reagiert dementsprechend darauf.

      Das heisst, der User kann nur die Ansicht / Anwendung auf seinem
      Rechner beenden durch den Stopp-Button, nicht aber auf dem Server.

      Das tut ja auch nicht der User, sondern der Server.

      Gruesse,
       CK

      1. Holladiwaldfee.

        Natürlich wird es nicht gestoppt,

        Das ist leider falsch. Dem Script wird erst ein SIGTERM und dann ein
        SIGKILL geschickt.

        Sache des Programmierers. In PHP kann man das z.B. mit ignore_user_abort(true) verhindern:http://www.php.net/manual/en/function.ignore-user-abort.php

        Ciao,

        Harry

        1. Hoi,

          Sache des Programmierers.

          Nein, ist es nicht. Man kann den SIGTERM abfangen, ja. Aber den SIGKILL
          kann man in keinem Fall abfangen.

          In PHP kann man das z.B. mit ignore_user_abort(true)
          verhindern:http://www.php.net/manual/en/function.ignore-user-abort.php

          Diese Funktion kann den SIGTERM abfangen.

          Gruesse,
           CK

          1. Holladiwaldfee.

            Sache des Programmierers.

            Nein, ist es nicht. Man kann den SIGTERM abfangen, ja. Aber den SIGKILL
            kann man in keinem Fall abfangen.

            In PHP kann man das z.B. mit ignore_user_abort(true)
            verhindern:http://www.php.net/manual/en/function.ignore-user-abort.php

            Diese Funktion kann den SIGTERM abfangen.

            Dann erklär mir doch bitte, wozu diese Funktion dann überhaupt gut ist. Wann wird SIGKILL gesendet und vom wem ? Wahrscheinlich vom Apache an sien PHP-Modul oder irgendeinen Kind-Prozess, wenn der auf SIGTERM nicht reagiert.

            Aber dann wäre ja der Sinn der Funktion ignore_user_abort völlig für'n Eimer, wenn gleich nach SIGTERM alles mit SIGKILL abgewürgt wird ?!

            Rätselnd,

            Harry

            1. Hoi,

              Dann erklär mir doch bitte, wozu diese Funktion dann überhaupt gut
              ist. Wann wird SIGKILL gesendet und vom wem?

              Vom Apachen an den Child-Prozess, nach dem dieser eine gewisse Zeit
              lang nicht auf SIGTERM reagiert.

              Wahrscheinlich vom Apache an sien PHP-Modul oder irgendeinen
              Kind-Prozess, wenn der auf SIGTERM nicht reagiert.

              Genau.

              Aber dann wäre ja der Sinn der Funktion ignore_user_abort völlig
              für'n Eimer, wenn gleich nach SIGTERM alles mit SIGKILL abgewürgt
              wird ?!

              Sozusagen. Sie gewinnt nur Zeit.

              Gruesse,
               CK

              1. Hollahü !

                Aber dann wäre ja der Sinn der Funktion ignore_user_abort völlig
                für'n Eimer, wenn gleich nach SIGTERM alles mit SIGKILL abgewürgt
                wird ?!

                Sozusagen. Sie gewinnt nur Zeit.

                So'n scheeeeß ! Na, trotzdem Danke für die Erklärung :-)

                Ciao,

                Harry
                (der sich gerade fragt, warum das net so im PHP-Manual steht) :-|

            2. Hi all!

              Rätselnd,

              genauso gehts jetzt mir...
              Was also muß ich tun, um ein Script (Perl), welches aufwändige Dateioperationen durchführt, vor dem "User zu schützen"? Ein Abbruch zwischendurch wäre schlimm. Ich werde mal versuchen, rauszukiegen, wie ich auf das SIGTERM im Script reagieren kann um damit das SIGKILL zu verhindern.

              Aber vielleicht kann mir ja auch hier einer einen Tipp geben?

              Auf die Schnelle werde ich heute abend mal einen Versuch auf dem Server machen, so wie es Sven vorgeschlagen hat. Dann kann ich vielleicht wieder ruhiger schlafen - oder auch nicht...

              Gruß Frank

              1. Hoi,

                Was also muß ich tun, um ein Script (Perl), welches aufwändige
                Dateioperationen durchführt, vor dem "User zu schützen"? Ein
                Abbruch zwischendurch wäre schlimm. Ich werde mal versuchen,
                rauszukiegen, wie ich auf das SIGTERM im Script reagieren kann um
                damit das SIGKILL zu verhindern.

                Du koenntest das folgende versuchen:

                use POSIX qw/setsid/;

                ....

                my $pid = fork;
                unless($pid) {
                  die 'could not fork!' unless defined $pid;

                # process is no longer a child
                  die 'could not remove session' unless setsid;
                }
                else {
                  # main process ends
                  exit 0;
                }

                Das sollte theoretisch gehen -- aber da ist keine Garantie!

                Gruesse,
                 CK

                1. Hi,

                  Du koenntest das folgende versuchen:

                  [...]

                  ich werde mir das ansehen und versuchen, es zu verstehen - ich habe noch nie 'geforkt'.
                  Jetzt ist erstmal Schluß für heute - morgen wieder.
                  Besten Dank jedenfalls an alle für die Antworten

                  Gruß Frank

                  1. Hoi,

                    ich werde mir das ansehen und versuchen, es zu verstehen - ich habe
                    noch nie 'geforkt'.

                    Sorry. Die Erklaerung habe ich vergessen. Also hier nachgereicht:

                    use POSIX qw/setsid/; # das Modul, das die POSIX-Schnittstelle nachbildet

                    ....

                    my $pid = fork; # hier wird ein vollkommen gleichwertiger Prozess erzeugt
                    unless($pid) { # $pid ist 0 im Child-Prozess, undefined wenn fork nicht geklappt hat
                      die 'could not fork!' unless defined $pid; # tja, wie gesagt

                    # process is no longer a child
                      die 'could not remove session' unless setsid; # (1)
                    }
                    else {
                      # main process ends
                      exit 0; # (2)
                    }

                    (1) Tja, hier wird setsid benutzt, um das Kind  aus der Prozessgruppe
                        herauszuloesen.

                    Prozesse sind unter Unix hierarchisch angeordnet. Das heisst, wenn du
                    dich einloggst auf deinem UNIX-System und ein 'ls' eingibst, dann
                    ist das 'ls' in Wahrheit schon ein Child, ein Child deiner Shell. Du
                    koenntest dir die Prozess-Struktur ungefaehr so vorstellen:

                    Kernel-Prozess
                      |
                      ---- login
                        |
                        ------- Shell
                          |
                          ---------- ls

                    Wenn du jetzt zufaellig ein sehr langes ls hast ;-), und du killst
                    die Shell, weil es dir zu lange dauert, dann wird der SIGTERM (oder
                    auch SIGKILL) nicht nur an die Shell geschickt, sondern auch an das
                    ls. Dasselbe passiert beim Apachen:

                    Kernel-Prozess
                      |
                      ------- Haupt-Apache-Prozess
                        |
                        ------- Kind-Apache fuer den Request
                          |
                          --------- CGI-Prozess
                            |
                            ---------- Kind des CGI-Prozesses

                    Das Signal hangelt sich also durch von CGI-Prozess zum Kind des
                    CGI-Prozesses. Deshalb muessen wir einen Weg finden, um setsid aus
                    der Prozess-Gruppe herauszuloesen und einen vollwertigen, eigenen
                    Haupt-Prozess daraus machen. Das uebernimmt setsid. Nach der
                    erfolgreichen Verwendung von setsid saehe die Prozess-Struktur so
                    aus:

                    Kernel-Prozess
                      |
                      +------ Haupt-Apache-Prozess
                      | |
                      | ------- Kind-Apache fuer den Request
                      |   |
                      |   --------- CGI-Prozess
                      |
                      |
                      ------- Kind des CGI-Prozesses

                    Theoretisch duerfte der Apache also kein Signal mehr an das Kind
                    schicken. Das ist der ganze 'Trick' bei der Sache.

                    Wer mehr will, soll sich 'Advanced Programming In The UNIX(TM)
                    Environment' kaufen ;-)

                    Gruesse,
                     CK

                    1. Hoi,

                      exit 0; # (2)

                      Tss, Fussnote 2 hab ich doch glatt vergessen...

                      Also:

                      (2) Hier wird der Haupt-Prozess beendet. Ein fork() musst du dir so
                          vorstellen:

                      Mutter-Prozess
                         beliebiger Code
                         .
                         .
                         .
                         fork()

                      ab hier sind aus dem Mutter-Prozess zwei Prozesse geworden: Parent-
                      und Child-Prozess. Beide(!!) fuehren erstmal grundsaetzlich denselben
                      Code aus, der Kind-Prozess ist lediglich eine komplette Kopie des
                      Mutter-Prozesses. Das muss der Programmierer abfangen. Dazu muss man
                      wissen, dass $pid im Mutter-Prozess die PID (Prozess-ID) des Kindes
                      enthaelt und der Child-Prozess 0 enthaelt.

                      Wichtig zu fork zu wissen waere vielleicht noch, dass du im
                      Haupt-Prozess (sofern du nicht mit setsid() einen neuen Haupt-Prozess
                      machst) auf den Kind-Prozess warten solltest (per wait()), weil du
                      sonst Zombies erzeugen kannst. Zombies sind Prozesse, die im Grunde
                      schon beendet sind: sie belegen keinerlei Ressourcen mehr, koennen
                      sich aber auch nicht endgueltig beenden, weil ihnen das OK vom
                      Kernel dazu fehlt.

                      Gruesse,
                       CK

                      1. Hallo Christian,

                        ein dickes DANKE für Deine Erklärungen, so grob war mir das Verhältnis parent - child - Prozess schon klar, nur wie ich zu einem child komme - also so rein programmtechnisch in Perl ;-) - das hatte ich mir noch nie angesehen. Jetzt werde ich damit ein wenig rumprobieren und dann mein CGI-Script entsprechend ändern.

                        Kernel-Prozess
                          |
                          +------ Haupt-Apache-Prozess
                          | |
                          | ------- Kind-Apache fuer den Request
                          |   |
                          |   --------- CGI-Prozess (1)
                          |
                          |
                          ------- Kind des CGI-Prozesses  (2)

                        Wenn ich mir das so ansehe, kommt aber doch noch eine Frage: Der Apache erwartet vom CGI-Script eine Ausgabe, sonst produziert er den beliebten 500er. Ich warte also im Parent-CGI-Prozess (1) auf das Ende des Child (2), mache dann meine Ausgabe und beende mit exit. Richtig?

                        Gruß Frank

                        1. Hoi,

                          ein dickes DANKE für Deine Erklärungen, so grob war mir das
                          Verhältnis parent - child - Prozess schon klar

                          Dir vielleicht, aber anderen evntl. nicht.

                          Kernel-Prozess
                            |
                            +------ Haupt-Apache-Prozess
                            | |
                            | ------- Kind-Apache fuer den Request
                            |   |
                            |   --------- CGI-Prozess (1)
                            |
                            |
                            ------- Kind des CGI-Prozesses  (2)

                          Wenn ich mir das so ansehe, kommt aber doch noch eine Frage: Der
                          Apache erwartet vom CGI-Script eine Ausgabe, sonst produziert er
                          den beliebten 500er. Ich warte also im Parent-CGI-Prozess (1) auf
                          das Ende des Child (2), mache dann meine Ausgabe und beende mit
                          exit. Richtig?

                          Nein ;-)
                          Der Child ist hier kein Child mehr. Du machst im Haupt-Prozess
                          deine Ausgabe und beendest ihn dann, ohne Ruecksicht auf Verluste.

                          Gruesse,
                           CK