Andreas Korthaus: Prozessverwaltung

Hallo!

Wollte mich dann auch mal mit der Prozessverwaltung in PHP mittels http://www.php3.de/manual/de/ref.pcntl.phpversuchen. Kennt vielleicht jemand eine gute und vor allem _einfach_zu_verstehende_ Quelle um mal ein paar Grundlagen zur Thread-/Prozessverwaltung von Linux zu lernen?
Ich verstehe nämlich leider nichtmal die kurze Erklärung im PHP-Manual komplett:

On success, the PID of the child process is returned in the parent's thread of execution, and a 0 is returned in the child's thread of execution. On failure, a -1 will be returned in the parent's context, no child process will be created, and a PHP error is raised.

OK: Bei Erfolg wird dem Eltern/Vater-Thread die Prozess-ID des Kinder-Prozesses zurückgegeben, eine "0" wird dem Kinder-Thread zurückgegeben, und ein "-1" bei Fehler. Dazu steht da folgendes Beispiel:

Beispiel 1. pcntl_fork() Example

<?php

$pid = pcntl_fork();
if ($pid == -1) {
     die("could not fork");
} else if ($pid) {
     // we are the parent
} else {
     // we are the child
}

?>

Was ich daran nicht verstehe, das eine Script hat erstmal nur einen Thread und einen Prozess. Dann wird durch fork ein Kinderprozess gestartet. Was ich nicht verstehe - wenn man mal den Fehler (-1) außen vorläßt - steht da eine Weiche, entweder $PID ist 0 oder hat eine Nummer > 0. Aber beides ist wahr, es gibt ja jetzt 2 Threads bzw. Prozesse, laufen dann beide Prozesse durch diese Weiche, der eine in die eine Richtung, der andere in die andere Richtung? Laufen dann also 2 Prozesse in ein und demselben Script? Da steht immer "gibt XY dem einen oder dem anderen Thread zurück", aber das Script gehört doch ausschließlich dem Vater-Thread, oder? Und anhand der PID kann man im Script drauf reagieren? Einmal geht das innderhalb der Weiche, klar, aber wenn ich danach auf einen speziellen Prozess zugreifen will, kann ich ja nicht mehr PID=0 verwenden, denn das gilt ja für alle Kinderprozesse, oder? Könnte man so auch später im Script alle Kinderprozesse gleichzeitig ansprechen? Über die PID kann man die Kinderprozesse ja nachdem ob die IF-Weiche zu Ende ist ja nur noch beenden, oder? Oder könnte ich auch später im Script einen speziellen Kinderprozess dazu veranlassen einen bestimmten PHP-Code auszuführen?

Und was ist der Unterschied zwischen einem Prozess und einem Thread? Prozess ist doch das was mir z.B. der Windows Task-Manager anzeigt, oder? Aber was ist jetzt ein Thread?

Ich versuche gerade nämlich mal ein Testscript zu schreiben, welches so viele Socketverbindungen wie möglich zu demselben externen Server herstellt(HTTP-GET Request). Vorab, was ist hier eigentlich der Vorteil vieler Prozesse, im Gegensatz zu einer Schleife in einem Prozess? PHP kann doch sowieso nur eine begrenzte Anzahl von Befehlern bearbeiten und wird die Prozesse doch auch in einer Warteschlange-schieben und nacheinander abarbeiten, oder? Oder muß man hierbei nicht jedesmal auf den Response des HTTP-Request warten?

Jedenfalls habe ich mir das so vorgestellt:

for($i=1;$i<10;$i++) {
    $pid = pcntl_fork();
    if ($pid == -1) {
         die("could not fork");
    } else if (!$pid) {
         for($j=1;$j<100;$j++) {
             fsockopen(...);
             fputs(...);
         }
    }
}

Würde das schon reichen, oder ist das Quatsch? Wieviele Prozesse verwendet man für so etwas erfahrungsgemäß?

Hat jemand schonmal pcntl-Funktionen bei PHP eingesetzt? Dazu muß ich doch PHP selbst kompilieren, mit --enable-pcntl wenn ich das richtig verstanden habe, oder?

Viele Grüße
Andreas

  1. Hallo Andreas,

    Kennt vielleicht jemand eine gute und vor allem
    _einfach_zu_verstehende_ Quelle um mal ein paar
    Grundlagen zur Thread-/Prozessverwaltung von
    Linux zu lernen?

    Unix-Linux-Systemprogrammierung (Herold, ADW-Verlag)
    Advanced Programming In The Unix(TM) Environment (Stevens, ADW-Verlag, Professional Computing Series)

    <?php

    $pid = pcntl_fork();
    if ($pid == -1) {
         die("could not fork");
    } else if ($pid) {
         // we are the parent
    } else {
         // we are the child
    }

    ?>

    Was ich daran nicht verstehe, das eine Script
    hat erstmal nur einen Thread und einen Prozess.

    Jups.

    Dann wird durch fork ein Kinderprozess
    gestartet.

    Jups.

    Was ich nicht verstehe - wenn man mal den
    Fehler (-1) außen vorläßt - steht da eine
    Weiche, entweder $PID ist 0 oder hat eine
    Nummer > 0.

    Jups.

    Aber beides ist wahr,

    Nein. Im einen Prozess ist das eine wahr, im
    anderen das andere.

    es gibt ja jetzt 2 Threads bzw. Prozesse,

    Zwei Prozesse. Ein sehr, sehr wichtiger
    Unterschied.

    laufen dann beide Prozesse durch diese Weiche,

    Ja.

    der eine in die eine Richtung, der andere in
    die andere Richtung?

    Ja.

    Laufen dann also 2 Prozesse in ein und
    demselben Script?

    fork() erstellt eine komplette Kopie des laufenden
    Prozesses.

    Da steht immer "gibt XY dem einen oder dem
    anderen Thread zurück",

    Hae?
    Man, verwechsele nicht Thread und Prozess. Das
    ist ein himmelweiter(!) Unterschied.

    aber das Script gehört doch ausschließlich dem
    Vater-Thread, oder?

    Hae?
    fork() macht folgendes: es erstellt eine
    *komplette* Kopie des aktuellen Prozesses.
    Einziger Unterschied: der PID-Variablen wird in
    der einen Kopie die PID des neuen Prozesses
    zugewiesen, der PID-Variablen in der anderen Kopie
    0. Danach wird ganz normal weitergemacht, als
    sei nichts geschehen.

    Und anhand der PID kann man im Script drauf
    reagieren?

    Korrekt.

    Einmal geht das innderhalb der Weiche, klar,
    aber wenn ich danach auf einen speziellen
    Prozess zugreifen will, kann ich ja nicht mehr
    PID=0 verwenden, denn das gilt ja für alle
    Kinderprozesse, oder?

    Im *Kindprozess* ist die PID 0. Dort kann man sie
    mit getpid(2) herausfinden. Im Vaterprozess ist
    sie eine (mehr oder weniger) zufaellige
    Integer-Zahl.

    Könnte man so auch später im Script alle
    Kinderprozesse gleichzeitig ansprechen?

    Nein, nur nacheinander.

    Über die PID kann man die Kinderprozesse ja
    nachdem ob die IF-Weiche zu Ende ist ja nur
    noch beenden, oder?

    Du kannst ueber kill(2) beliebige Signale (siehe
    signal(3)) schicken.

    Oder könnte ich auch später im Script einen
    speziellen Kinderprozess dazu veranlassen
    einen bestimmten PHP-Code auszuführen?

    Wenn du mit Pipes arbeitest oder mit Signalen,
    klar, warum nicht?

    Und was ist der Unterschied zwischen einem
    Prozess und einem Thread?

    Ein Prozess hat einen komplett eigenen Adressraum,
    also eigene Variablen, eigenen Speicher, etc.
    Ein Thread ist nur ein etwas im selben Programm,
    hat denselben Adressraum. Einfachste Auswirkung:
    ein Thread hat z. B. keine PID. Es ist kein
    eigener Prozess. Deshalb verbraucht er auch viel
    weniger Ressourcen.

    Ja, ich weiss, unter Linux sieht man (noch)
    Threads in den Prozess-Listen. Das liegt daran,
    dass Threads unter Linux keine Kernelthreads sind,
    sondern abgespeckte (lightweight) Prozesse, die
    ueber das memory filesystem miteinander
    kommunizieren. Ist natuerlich sehr langsam, das
    ganze.

    Vorab, was ist hier eigentlich der Vorteil
    vieler Prozesse, im Gegensatz zu einer
    Schleife in einem Prozess?

    Ehm, denk doch mal nach :) Ein einziger Prozess
    ist synchron. Mehrere Prozesse sind asynchron.
    Ein einziger Prozess kann nur einen Request nach
    dem anderen machen. Mehrere Prozesse koennen
    jeweils einen Request (fast) simultan mit den
    anderen machen.

    PHP kann doch sowieso nur eine begrenzte Anzahl
    von Befehlern bearbeiten und wird die Prozesse
    doch auch in einer Warteschlange-schieben und
    nacheinander abarbeiten, oder?

    Was hat das mit PHP zu tun?

    Oder muß man hierbei nicht jedesmal auf den
    Response des HTTP-Request warten?

    Hae?

    for($i=1;$i<10;$i++) {
        $pid = pcntl_fork();
        if ($pid == -1) {
             die("could not fork");
        } else if (!$pid) {
             for($j=1;$j<100;$j++) {
                 fsockopen(...);
                 fputs(...);
             }
        }
    }

    Würde das schon reichen, oder ist das Quatsch?

    Prinzipiell reicht das.

    Wieviele Prozesse verwendet man für so etwas
    erfahrungsgemäß?

    Was hast du vor?

    Gruesse,
     CK

    1. Hi Christian!

      Unix-Linux-Systemprogrammierung (Herold, ADW-Verlag)
      Advanced Programming In The Unix(TM) Environment (Stevens, ADW-Verlag, Professional Computing Series)

      Gibts da nichts online? Ist das Thema denn soooo komplex?

      Ein Prozess hat einen komplett eigenen Adressraum,
      also eigene Variablen, eigenen Speicher, etc.
      Ein Thread ist nur ein etwas im selben Programm,
      hat denselben Adressraum. Einfachste Auswirkung:
      ein Thread hat z. B. keine PID. Es ist kein
      eigener Prozess. Deshalb verbraucht er auch viel
      weniger Ressourcen.

      Also kann man dann auch innerhalb eines Prozesses mehrere Threads starten? Bezogen auf mein Beispiel unten, könnte ich anstatt neue Prozesse zu starten genauso neue Threads innerhalb des einen Prozesses starten, mit selbigem Ergebnis, also dass die HTTP-Requests parallel versendet werden, bei niedrigerer Hardware-Auslastung? Geht das überhaupt bzw. geht das mit PHP?

      Ja, ich weiss, unter Linux sieht man (noch)
      Threads in den Prozess-Listen. Das liegt daran,
      dass Threads unter Linux keine Kernelthreads sind,
      sondern abgespeckte (lightweight) Prozesse, die
      ueber das memory filesystem miteinander
      kommunizieren. Ist natuerlich sehr langsam, das
      ganze.

      Heißt das man sollte das bei Linux doch lieber lassen?

      Ehm, denk doch mal nach :) Ein einziger Prozess
      ist synchron. Mehrere Prozesse sind asynchron.

      Ja, aber das ändert ja erstmal nicht daran das die CPU damit auch nicht schneller wird. Wird da also irgendwo was gespart.

      Ein einziger Prozess kann nur einen Request nach
      dem anderen machen. Mehrere Prozesse koennen
      jeweils einen Request (fast) simultan mit den
      anderen machen.

      Was meinst Du hier mit Request? Einen HTTP-Request wie unten beschreiben?

      PHP kann doch sowieso nur eine begrenzte Anzahl
      von Befehlern bearbeiten und wird die Prozesse
      doch auch in einer Warteschlange-schieben und
      nacheinander abarbeiten, oder?

      Was hat das mit PHP zu tun?

      Ich kenne mich da jetzt wirklich nicht aus, nur, wenn ich jetzt PHP verwende, also ich erstelle 10 Kinderprozesse, greifen diese Prozesse dann alle über die gegebenen Schnittstellen direkt auf OS-Funkionene zu, z.B. um einen HTTP-Request abzusenden? ich hatte jetzt gedacht das da der PHP Interpreter oder sowas noc dazwischen hängt, aber das Script ist ja, wenn es bereits ausgeführt wird schon interpretiert, oder?

      Oder muß man hierbei nicht jedesmal auf den
      Response des HTTP-Request warten?

      Hae?

      Wenn ich in einem Script in einer schleife fsockopen, fputs("GET..."), fclose verwende, läuft die Schleife erst weiter, wenn die Antwort vom Server gekommen ist, wenn ich das auf mehrere Prozesse verteile, können alle (fast) gleichzeitig anfangen, ohne auf andere Prozesse Rücksicht nehmen zu müssen.

      for($i=1;$i<10;$i++) {
          $pid = pcntl_fork();
          if ($pid == -1) {
               die("could not fork");
          } else if (!$pid) {
               for($j=1;$j<100;$j++) {
                   fsockopen(...);
                   fputs(...);
               }
          }
      }

      Was hast du vor?

      Ich würde gerne mal ein paar möglichst realistische Performance-Vergleiche machen. Hierzu habe ich einige Server in verschiedenen voneinander getrennten Netzen, von denen aus ich je ein Script starten möchte, welches möglichst viele HTTP-Requests an den zu testenden Server sendet. Mit der Schleife alleine, ohne fork, komme ich nicht sehr weit, daher hoffe ich das ich das ganze mit fork beschleunigen kann. Ich weiß noch nicht wo jetzt genau die Engpässe liegen, ich vermute aber das durch das Warten auf die Antwort des HTTP-Requests die meiste Zeit verschwendet wird.

      Viele Grüße
      Andreas

      1. Hallo Andreas,

        Unix-Linux-Systemprogrammierung (Herold, ADW-Verlag) Advanced Programming In The Unix(TM) Environment (Stevens, ADW-Verlag, Professional Computing Series) Gibts da nichts online? Ist das Thema denn soooo komplex?

        Ersteres habe ich auch, das hat 1147 Seiten. ;-)

        Also kann man dann auch innerhalb eines Prozesses mehrere Threads starten?

        Ja. Du musst aber im Prozess einen eigenen Scheduler haben. Bei normalen Prozessen sieht das so aus:

        +--------------------------------------------------------------------------+ |                                Kern                                      | |     +--------------------------------------------------------+           | |     |   Scheduler                                            |           | |     +--------------------------------------------------------+           | |        ^                 ^                      ^                        | |        |                 |                      |                        | +--------------------------------------------------------------------------+          |                 |                      |          v                 v                      v       +-------------+  +-------------+        +-------------+       | Prozess     |  | Prozess     |        | Prozess     |       +-------------+  +-------------+        +-------------+

        Wenn Du Threas innerhalb eines Pozesses haben willst, dann brauchst Du innerhalb des Prozesses einen eigenen Scheduler:

        +--------------------------------------------------------------------------+ |                                Kern                                      | |     +--------------------------------------------------------+           | |     |   Scheduler                                            |           | |     +--------------------------------------------------------+           | |        ^                 ^                      ^                        | |        |                 |                      |                        | +--------------------------------------------------------------------------+          |                 |                      |          v                 v                      |       +-------------+  +-------------+   +--------------------------------------+       | Prozess     |  | Prozess     |   |        |            Prozess          |       +-------------+  +-------------+   |        v                             |                                          |   +--------------+      +---------+  |                                          |   | Scheduler    | <--> | Thread  |  |                                          |   +--------------+      +---------+  |                                          |      ^          ^                    |                                          |      |          `----                |                                          |      v               \               |                                          |    +-----------+     |               |                                          |    | Thread    |     v               |                                          |    +-----------+  +------------+     |                                          |                   | Thread     |     |                                          |                   +------------+     |                                          |                                      |                                          +--------------------------------------+

        In der IX 12/2002 auf Seite 110 wird eine neue Thread-Biblithek für Linux vorgestellt, die das o.g. kann. Diese ist aber noch "experimentell".

        Bezogen auf mein Beispiel unten, könnte ich anstatt neue Prozesse zu starten genauso neue Threads innerhalb des einen Prozesses starten, mit selbigem Ergebnis, also dass die HTTP-Requests parallel versendet werden, bei niedrigerer Hardware-Auslastung?

        Theoretisch: ja.

        Geht das überhaupt bzw. geht das mit PHP?

        Mit PHP: Da müsstest Du schon eine eigene Erweiterung schreiben. Denn PHP selbst kann so etwas nicht. PHP kennt AFAIK nicht mal die Linuxthreads-Implementierung, sondern nur die "normalen" Prozessfunktionen. (ich kann mich aber auch täuschen)

        Heißt das man sollte das bei Linux doch lieber lassen?

        Wie gesagt, PHP kennt sowieso nur Pozessfunktionen, aber keine Threads, und dann macht es keinen Unterschied.

        Ja, aber das ändert ja erstmal nicht daran das die CPU damit auch nicht schneller wird. Wird da also irgendwo was gespart.

        Nein, aber eine CPU unterstützt das Betriebsystem im Multitasking. Während eine Aktion läuft, kann umgeschaltet werden, so dass diese Aktion unterbrochen wird, bis dieser Prozess wieder drankommt.

        Ich kenne mich da jetzt wirklich nicht aus, nur, wenn ich jetzt PHP verwende, also ich erstelle 10 Kinderprozesse, greifen diese Prozesse dann alle über die gegebenen Schnittstellen direkt auf OS-Funkionene zu, z.B. um einen HTTP-Request abzusenden? ich hatte jetzt gedacht das da der PHP Interpreter oder sowas noc dazwischen hängt, aber das Script ist ja, wenn es bereits ausgeführt wird schon interpretiert, oder?

        Natürlich hängt PHP dazwischen, aber jedes Mal eine andere PHP-Instanz, weil das alles andere Prozesse sind.

        Wenn ich in einem Script in einer schleife fsockopen, fputs("GET..."), fclose verwende, läuft die Schleife erst weiter, wenn die Antwort vom Server gekommen ist, wenn ich das auf mehrere Prozesse verteile, können alle (fast) gleichzeitig anfangen, ohne auf andere Prozesse Rücksicht nehmen zu müssen.

        Genau.

        Grüße,

        Christian

        --
        Sollen sich alle schämen, die gedankenlos sich der Wunder der Wissenschaft und Technik bedienen und nicht mehr davon erfasst haben als eine Kuh von der Botanik der Pflanzen, die sie mit Wohlbehagen frisst.                       -- Albert Einstein