Andreas-Lindig: file-locking um konkurrierende Datei-Zugriffe zu regeln?

Hallo Forum,

ich soll file-locking betreiben, um konkurrierende Zugriffe auf eine Datei, die auch immer überschrieben wird, zu vermeiden. Nun kann ich da aber in  PHP gar nichts zu finden. Außerdem dachte ich immer: wenn eine Datei geöffnet wird, ist sie automatisch für andere Benutzer gesperrt. Stimmt das nicht?

wie ist das konkret mit 'file()' und 'fopen()'?

Gruß, Andreas

--
<img src="http://was-ist-das.andreas-lindig.de/was_ist_das_fetzen.jpg" border="0" alt="">
hier könnte auch ruhig mal'n neues Bild stehen.
  1. Hallo Andreas,

    ich soll file-locking betreiben, um konkurrierende Zugriffe auf eine Datei, die auch immer überschrieben wird, zu vermeiden. Nun kann ich da aber in  PHP gar nichts zu finden. Außerdem dachte ich immer: wenn eine Datei geöffnet wird, ist sie automatisch für andere Benutzer gesperrt. Stimmt das nicht?

    wie ist das konkret mit 'file()' und 'fopen()'?

    es gibt flock() mit Parametern (müsstest du mal nachschauen/googlen), z.B.:

    fopen(..
    flock(.. 2)
    fwrite(..
    flock(.. 3)
    fclose(..

    Grüsse

    Cyx23

    1. Hi!

      fopen(..
      flock(.. 2)

      Hier zwischen können andere Prozesse auf die Datei aber noch zugreifen (z.B. könnten 2 Apache-Prozesse denselben Wert aus einer Counter-Datei auslesen). Ist zwar sehr unwahrscheinlich, aber möglich.

      Daher empfehle ich eine zusätzliche lock-Datei, also eine einfache Datei ohne Inhalt, wenn die da ist darf nicht zugegriffen werden. Zumindest bringt das Datenkonsitenz (wenn es darum geht), aber man sollte sich dann auch um eine vernünftige Fehlerbehandlung kümmern, bzw. mehrere Versuche ermöglichen. Auch kann es vorkommen dass die Datei mal aus irgendeinem Grund nicht gelöscht wurde, und dann kann man warten bis man schwarz wird -> dead-lock. Wenn die Zugiffe sehr lange dauern ist diese Methode aber auch problematisch.

      Eine wirklich saubere/einfache Lösung um einen ID-Generator (der garantiert keine doppelten IDs erzeugt) mit PHP zu programmieren habe ich bisher nicht gefunden, es sei denn man überlässt das Problem der konkurrierenden Zugriffe einer hierfür optimierten Software wie einer DB. Oder man schreibt einen Dämon-Prozess, der dann systemweit nur in einer Instanz läuft, und die IDs per IPC oder Socket herausgibt :) Dann ist es wiederum sehr einfach. Oder man verwendet SHM und Semaphore (http://de3.php.net/sem), direkt aus PHP heraus... daber das hat jetzt vermutlich so ziemlich gar nichts mehr mit dem ursprünglichen Problem zu tun ;-)

      Grüße
      Andreas

      --
      SELFHTML Tipps & Tricks: http://aktuell.de.selfhtml.org/tippstricks/
      1. Hello,

        fopen(..
        flock(.. 2)
        Hier zwischen können andere Prozesse auf die Datei aber noch zugreifen (z.B. könnten 2 Apache-Prozesse denselben Wert aus einer Counter-Datei auslesen). Ist zwar sehr unwahrscheinlich, aber möglich.

        Ist sogar sehr wahrscheinlich bei hoher Besucherfrequenz...

        Siehe hierzu die Strategien zu "optimistic Locking" und "pessimistic Locking"

        Außerdem muss der gesamt Update-Prozess durch ein Exclusive-Lock geschirmt werden, dann kann sowas nicht passieren.

        Liebe Grüße aus http://www.braunschweig.de

        Tom

        --
        Fortschritt entsteht nur durch die Auseinandersetzung der Kreativen
        Nur selber lernen macht schlau
        1. Hi Tom!

          fopen(..
          flock(.. 2)
          Hier zwischen können andere Prozesse auf die Datei aber noch zugreifen (z.B. könnten 2 Apache-Prozesse denselben Wert aus einer Counter-Datei auslesen). Ist zwar sehr unwahrscheinlich, aber möglich.

          Ist sogar sehr wahrscheinlich bei hoher Besucherfrequenz...

          Ja, auf lange Sicht sicherlich.

          Außerdem muss der gesamt Update-Prozess durch ein Exclusive-Lock geschirmt werden, dann kann sowas nicht passieren.

          Was aber in PHP nicht ganz so einfach ist, oder? Was meinst Du jetzt mit "Update-Prozess"? Doch die "Prozedur" des Aktualisierens selber, oder?

          Mal angenommen Du wolltest eine Funktion schreiben, die wie ein Counter steigende Nummern zurückgibt, meinetwegen:

          $next_id = get_next_id();

          Die Id soll wie bei MySQLs Auto-Increment bei jedem Aufruf immer um 1 erhöht werden. Das Script mit der Funktion wird von verschiedenen Apache-Prozessen konkurrierend aufgerufen.

          Wie würdest Du das jetzt implementieren, das heißt sicherstellen dass es keine doppelten IDs gibt (was ja passiert wenn die Datei mit der aktuellen ID von einem 2. Prozessen zw. fopen() und flock() aufgerufen wird )?

          Eine externe Lock-Datei hat IMHO gerade bei besonders vielen Zugriffen den Nachteil, dass es zu einem Dead-Lock kommen kann, falls ein Prozess stirbt...
          Ist sowas mit PHP über eine Datei überhaupt möglich?

          Grüße
          Andreas

          --
          SELFHTML Tipps & Tricks: http://aktuell.de.selfhtml.org/tippstricks/
          1. Hello,

            Was aber in PHP nicht ganz so einfach ist, oder? Was meinst Du jetzt mit "Update-Prozess"? Doch die "Prozedur" des Aktualisierens selber, oder?

            Doch das geht auch mit PHP. Lies Dir nochmal meine Antwort oben durch

            Mal angenommen Du wolltest eine Funktion schreiben, die wie ein Counter steigende Nummern zurückgibt, meinetwegen:

            $next_id = get_next_id();

            Die Id soll wie bei MySQLs Auto-Increment bei jedem Aufruf immer um 1 erhöht werden. Das Script mit der Funktion wird von verschiedenen Apache-Prozessen konkurrierend aufgerufen.

            Das geht nur, wenn alle Prozesse dieselben Regeln berücksichtigen, am besten dasselbe "API-Script". Sonst benötigt man mandatory Locking.

            Wie würdest Du das jetzt implementieren, das heißt sicherstellen dass es keine doppelten IDs gibt (was ja passiert wenn die Datei mit der aktuellen ID von einem 2. Prozessen zw. fopen() und flock() aufgerufen wird )?

            Das habe ich oben schon beschrieben:

            Datei öffnen Handle besorgen
              Exclusive Lock beantragen
              Zähler auslesen
              Zähler incrementieren und merken
              Zähler zurückschreiben
              Datei schließen

            Schlüssel zurückliefern

            Außer, dass da unerwünschte Dateioperationen (rm, chmod, mv) oder ein Connection Loss stattfinden können, kann nichts schlimmes passieren. Und dass diese Veränderungen nicht stattfinden, dafür muss man sorgen und für den Fall dass doch, immer die Fehler abfragen.

            Das Verfahren basiert natürlich auf "Lost Keys". Das bedeutet, dass ungenutzte Schlüssel einfach verloren gehen.

            Eine externe Lock-Datei hat IMHO gerade bei besonders vielen Zugriffen den Nachteil, dass es zu einem Dead-Lock kommen kann, falls ein Prozess stirbt...

            Wenn ein Prozess stirbt, gibt PHP automatisch die Handles zurück und das OS löst automatisch die Locks wieder auf.

            Liebe Grüße aus http://www.braunschweig.de

            Tom

            --
            Fortschritt entsteht nur durch die Auseinandersetzung der Kreativen
            Nur selber lernen macht schlau
            1. Hi Tom!

              Doch das geht auch mit PHP. Lies Dir nochmal meine Antwort oben durch

              hab ich ;-)

              Das geht nur, wenn alle Prozesse dieselben Regeln berücksichtigen, am besten dasselbe "API-Script". Sonst benötigt man mandatory Locking.

              Ja, das könnte man ja sicherstellen. "mandatory Locking" kann PHP ja nicht.

              Wie würdest Du das jetzt implementieren, das heißt sicherstellen dass es keine doppelten IDs gibt (was ja passiert wenn die Datei mit der aktuellen ID von einem 2. Prozessen zw. fopen() und flock() aufgerufen wird )?

              Das habe ich oben schon beschrieben:

              Datei öffnen Handle besorgen
                Exclusive Lock beantragen

              Zähler auslesen
                Zähler incrementieren und merken
                Zähler zurückschreiben

              Ja, aber wie mache ich das mit einem handle? Meinst Du hier das handle welches LOCK "beantragt" hat?

              $fp = fopen ('test.txt','a+');
              $id = fread($fp,32);
              fseek($fp,0,SEEK_SET);
              fputs($fp, ++$id);

              Geht nicht.

              Wenn ich die neue ID zurückschreiben will, muss ich den lock lösen, das handle schließen, und ein neues besorgen und erneut locken. Dazwichen kann ne Menge passieren. Oder gibt es da doch eine Möglichkeit mit nur einem handle? Bedenke dass ich z.B. 123 auslese, und am Ende soll 124 drin stehen.

              Datei schließen

              Schlüssel zurückliefern

              Außer, dass da unerwünschte Dateioperationen (rm, chmod, mv) oder ein Connection Loss stattfinden können, kann nichts schlimmes passieren. Und dass diese Veränderungen nicht stattfinden, dafür muss man sorgen und für den Fall dass doch, immer die Fehler abfragen.

              Ja, das ist nicht das Problem.

              Eine externe Lock-Datei hat IMHO gerade bei besonders vielen Zugriffen den Nachteil, dass es zu einem Dead-Lock kommen kann, falls ein Prozess stirbt...

              Wenn ein Prozess stirbt, gibt PHP automatisch die Handles zurück und das OS löst automatisch die Locks wieder auf.

              ich dachte dran, eine extra Datei anzulegen, und immer wenn diese Datei vorhanden ist, dann gilt die Datendatei als gesperrt. Wenn aber ein PHP-Prozess stirbt ohne dieses Datei gelöscht zu haben, ist das Mist, würde ich also nicht machen sowas.

              Die einzige sichere Möglichkeit die mir gerade wieder eingefallen ist funktioniert so:

              $lf = 'LOCK';     // Lock-Datei (leer)
                $cf = 'counter';  // Counter-Datei (enthält ID)

              // externes LOCK-File sperren
                $lp = fopen($lf,'r');
                flock($lp,LOCK_EX);

              // eigentliche counter-Operation durchführen
                $fp = fopen($cf,'r');
                $id = fread($fp,32);
                fclose($fp);
                $fp = fopen($cf,'w');
                fputs($fp,++$id);
                fclose($fp);

              // externes LOCK-File entsperren
                flock($lp,LOCK_UN)
                fclose($lp);

              wenn sich alle hieran halten sollte der counter 100% konsistent funktionieren.

              Grüße
              Andreas

              --
              SELFHTML Linkverzeichnis: http://aktuell.de.selfhtml.org/links/
              1. Hello,

                Das geht nur, wenn alle Prozesse dieselben Regeln berücksichtigen, am besten dasselbe "API-Script". Sonst benötigt man mandatory Locking.
                Ja, das könnte man ja sicherstellen. "mandatory Locking" kann PHP ja nicht.

                Da bin ich mir noch nicht endgültig sicher. PHP hat die "dio"-Funktionen und die sisn schon dicht dran. Aber wahrscheinlich wird das der Lockmode genauso gefiltert, wie im "normalen" C. Da muss man auch eine extra Extension laden, wenn man ihn benutzen will. Das bezieht sich jetzt auf die PC-OS-Konzepte, die an dieser Stelle alle ähnlich gestrickt sind.

                Ja, aber wie mache ich das mit einem handle? Meinst Du hier das handle welches LOCK "beantragt" hat?

                $fp = fopen ('test.txt','a+');   # a+ (ab+) , damit die Datei nicht
                                                   # gelöscht wird und kein Handlewechsel
                                                   # statttfinden muss
                  $lock = flock($fp, LOCK_EX);     # hält das programm an dieser Stelle
                                                   # solange an, bis LOCK erfolgreich
                                                   # oder timeout
                  $fseek($fp,0,SEEK_SET);          # Dateizeiger auf Anfang zurückstellen

                $id = fread($fp,32);             # Datei auslesen
                fseek($fp,0,SEEK_SET);           # Zeiger wieder auf Anfang stellen

                ftruncate($fp,0);                # File leeren

                fputs($fp, ++$id);               # File neu schreiben.

                fclose($fp);

                Geht so

                Außerdem habe ich da noch Lösungsansätze auf der seite http://selfhtml.bitworks.de stehen. In dem Entwurf von "flat_box" ist auch eine Lockfunktion drin, die nicht-sperrend arbeitet und bisher keinerlei Fehler verursacht hat. man muss natürlich das Ergebnis auch auswerten.

                Wenn ich die neue ID zurückschreiben will, muss ich den lock lösen, das handle schließen, und ein neues besorgen und erneut locken. Dazwichen kann ne Menge passieren. Oder gibt es da doch eine Möglichkeit mit nur einem handle? Bedenke dass ich z.B. 123 auslese, und am Ende soll 124 drin stehen.

                Eben das darf man nicht machen. Ein aufgeben des Handles würde auch das Lock ungültig machen. man muss dabei zwischen den lokalen Handles des Prozesses und den zentralen Handles des OS untrscheiden.

                Liebe Grüße aus http://www.braunschweig.de

                Tom

                --
                Fortschritt entsteht nur durch die Auseinandersetzung der Kreativen
                Nur selber lernen macht schlau
                1. Hi Tom!

                  [...]
                  Geht so

                  <?php
                  $fp = fopen ('test.txt', 'a+');
                  $lock = flock($fp, LOCK_EX);
                  rewind($fp);
                  $id = fread($fp, 32);
                  rewind($fp);
                  ftruncate($fp, 0);
                  fputs($fp, ++$id);
                  flock($fp, LOCK_UN);
                  fclose($fp);
                  ?>

                  Ja, in der Tat, mit "ftruncate()" hatte ich noch nie gearbeitet ;-)

                  Danke für den Tipp, wird mir sicher helfen!

                  Grüße
                  Andreas

                  --
                  SELFHTML Linkverzeichnis: http://aktuell.de.selfhtml.org/links/
  2. Hallo,

    ich soll file-locking betreiben, um konkurrierende Zugriffe auf eine Datei, die auch immer überschrieben wird, zu vermeiden. Nun kann ich da aber in  PHP gar nichts zu finden. Außerdem dachte ich immer: wenn eine Datei geöffnet wird, ist sie automatisch für andere Benutzer gesperrt. Stimmt das nicht?

    Also ich habe mich damit noch nicht ausführlich beschäftigt, aber ich denke http://at.php.net/manual/en/function.flock.php dürfte dir weiterhelfen :-)

    gruß,
    Severin

    --
    Realität ist das, was nicht verschwindet, wenn man aufhört, daran zu glauben.
    --Philip K. Dick
  3. Hello,

    ich soll file-locking betreiben, um konkurrierende Zugriffe auf eine Datei, die auch immer überschrieben wird, zu vermeiden. Nun kann ich da aber in  PHP gar nichts zu finden. Außerdem dachte ich immer: wenn eine Datei geöffnet wird, ist sie automatisch für andere Benutzer gesperrt. Stimmt das nicht?

    wie ist das konkret mit 'file()' und 'fopen()'?

    Solange eine Dateioperation läuft, kann keine andere laufen. Allerdings muss man das "microskopisch" betrachten. Wenn mit file() Daten ausgelesen werden, wird das in Blöcken gemacht, da die gesamte Datei wahrschinlich nicht auf einmal in den IO-buffer passt (@CK... da war doch gerade was?). Zwischen diesen einzelnen Leseaufrufen von der Platte in den Puffer liegen nun zeitliche Lücken. Wenn die Datei also nicht für die Gesamtdauer aller Leseaufrufe für andere gesperrt wird, könnten Andere zwischendurch einen Schreibbefehl absetzen und dann würden sich ggf. beide Prozesse gegenseitig stören. Das liegt im Wesentlichen daran, dass die physischen "Puffergrenzen" nicht mit den logischen Satzgrenzen übereinstimmen.

    Da ich den PHP-Programmierern viel Gutes zutraue, nehem ich mal an, dass während des Leseprozesses mit file() die Datei für andere gesperrt ist.

    Das nütz uns aber nichts, wenn wir ein Update auf einen Bereich (Datensatz) in der Datei ausüben wollen. Dann müssten wir den gesamten Update-Prozess abschirmen.

    Theoretisch würde das so aussehen.

    1. Datei sperren
    2. Datei öffnen
    3. Datei lesen
    4. Veränderung durchführen
    5. Datei schreiben
    6. Datei schließen
    7. Datei entsprerren

    PHP unterstützt aber in den normalen Filefunktionen kein mandatory Locking (google) sondern nur advisory Locking.

    Beim mandatory Locking sind i.d.R der Sperr- und der Öffnungsprozess in einem Betriebssystemaufruf zusammengefasst. Das verhindert "Lücken" im zeitlichen Ablauf.

    Beim advisory Locking haben Dateioperation (öffen, lesen, schreiben, schließen, Zeiger bewegen, abschneiden,...) und Locking nichts miteinander zu tun. Die einzige Verbindung zwischen diesen beiden getrennten Prozessen wird über das Dateihandle hergestellt.

    Das Handle wird für das Lockung benutzt, um die Datei zu kennzeichnen. In einer separaten Tabelle wird eingetragen, welcher Prozess (User) gerade welche Lockingstufe für welche Datei wünscht. Wenn man also Dateioperationen durchführen will, muss man sich erst ein handle auf die Datei besorgen (fopen() ) und dann nachfragen, ob man ein Lock auf die Datei bekommen kann. Da dies nur beratende Wirkung hat, kann man trotzdem versuchen alle Dateioperationen durchzuführen. Vielleicht trifft man gerade eine "Lücke" und schon ist es passiert. Es müssen also alle Programme, die auf die gemeinsamen Dateien zugreifen, auch dieselben Regeln beachten.

    Es gibt zwei aktive Locking-Stufen:

    LOCK_SH: verhindert, dass jemand Anderes ein LOCK_EX bekommt
               wird nur ertielt, wenn kein LOCK_EX eines
               anderen Usersvorliegt
               ein vorhandenes LOCK_SH eines anderen Users
               stört nicht

    LOCK_EX: verhindert, dass jemand anderes ein LOCK_SH
               oder LOCK_EX bekommt.
               wird nur erteilt, wenn überhaupt kein LOCK eines
               anderen Users vorlag. Ein eigenes LOCK_SH stört nicht.

    Die einfachste Strategie ist es nun, zum ausschließlichen Lesen von Dateien immer ein LOCK_SH zu beantragen und wenn man auch Daten schreiben will, immer ein LOCK_EX zu beantragen.

    Filehandle besorgen
      Datei EXCLUSIV sperren lassen
      Daten lesen
      Daten ändern
      Daten schreiben
      Datei schließen

    Durch das Schließen wird das LOCK automatisch aufgehoben.
    Der gesamte Prozess oben sollte so kompakt (also kurz) wie möglich gehalten werden.

    Wie geht man nun vor, wenn man einen Datensatz ändern will?

    Dies erfordert für den optimierten konkurrierenden Betrieb eine speziell angepasste Dateistruktur. In satzorientierten Dateien muss jeder Datensatz einen Konflikt-Zähler (oft reicht ein Timestamp) tragen.

    Vorgangsbeschreibung

    Handle besorgen
      Datei read-Lock
      Datei lesen
      datei schließen

    Page mit Auswahlliste an den Client senden

    Client fordert einzelnen Satz zum Lesen an

    Handle besorgen
      Datei read-Lock
      Datei lesen (mit den aktuellen Timestamps)
      datei schließen

    Einezldatensatz mit Formular an Client senden

    Client führt Veränderungen durch und fordert Update an

    Handle besorgen
      Datei Exclusive lock
      Datei lesen
      Datensatz raussuchen
      Timestamps vergleichen
      bei Gleichheit den Satz austauschen, Timestamp aktualisieren,
        OK setzen, bei Ungleichheit NotOK setzen und nichts machen

    Daten zurückschreiben
      Datei schließen

    OK:    dem Client die aktualisierte Auswahlliste schicken
      NotOK: dem Client den eigenen Datensatz UND den aktuellen aus der
             Datei in einem Doppelformular zurückschicken mit ZWEI submit-         Buttons. Der Client kann nun entscheiden, in welchem der beiden
             Vorlagen er seine Änderungen vornimmt. Beim Update wird aber
             auf jeden Fall der Timestamp des letzten gelesenen Satzes
             benutzt.

    Das Verfahren nennt man "optimistic Locking", da man ersteinmal davon
      ausgeht, dass zwischen Lesen der Vorlagedaten und Rückschreiben nichts
      weiter passiert ist.

    Es gibt auch das "pessimistic Locking". Hier wird ein Satz gezielt für
      die Veränderung angefordert und in diesem ein Merker gesetzt, welcher
      User gerade das Ändern angefordert hat. Solange dieser Merker nicht
      beseitigt wurde, kann kein anderer User den Satz bearbeiten.
      Bei HTTP ist dieses Verfahren nicht ohen weiteres verwendbar, da
      massenweise lost Flags entstehen könnten.

    Die Funktion file() eignet sich alleine nicht zum Konkurrierenden Betrieb, da sie kein Handle liefert. Aber man kann einen Trick anwenden und ein seperates Lockfile verwenden. siehe hierzu http://selfhtml.bitworks.de "speichern". Das Lockfile kann eine Dummydatei sein oder auch eine mit Funktion, z.B. das LOG-File der Applikation.

    ICh hoffe, ich kpnnte Dir helfen...

    Liebe Grüße aus http://www.braunschweig.de

    Tom

    --
    Fortschritt entsteht nur durch die Auseinandersetzung der Kreativen
    Nur selber lernen macht schlau
    1. Hallo Tom,

      ICh hoffe, ich kpnnte Dir helfen...

      hoffe ich auch ,-) Das hat mich erstmal erschlagen. Deshalb auch keine Reaktion bis jetzt. Ich brauch noch Zeit zum Probieren. Aber eins verstehe ich nicht: Auch in der Doku steht was, daß die Prozesse die gleichen Regeln beachten müssen oder so. Wie stelle ich das denn fest? Also bei mir greifen nicht verschiedene Programme auf eine Datei zu, sondern das eine Script eben von verschiedenen Clients angefordert.

      Gruß, Andreas

      --
      <img src="http://was-ist-das.andreas-lindig.de/was_ist_das_fetzen.jpg" border="0" alt="">
      hier könnte auch ruhig mal'n neues Bild stehen.
      1. Hello Andreas,

        hoffe ich auch ,-) Das hat mich erstmal erschlagen. Deshalb auch keine Reaktion bis jetzt. Ich brauch noch Zeit zum Probieren. Aber eins verstehe ich nicht: Auch in der Doku steht was, daß die Prozesse die gleichen Regeln beachten müssen oder so. Wie stelle ich das denn fest? Also bei mir greifen nicht verschiedene Programme auf eine Datei zu, sondern das eine Script eben von verschiedenen Clients angefordert.

        Alle Filezugriffe werden (gottseidank) immer noch über das Betriebsystem abgewickelt. Das Betriebssystem führt dazu eine Handletabelle. Bei Multiuser-Systemen ist die schon ganz schön komplex und irgendwo in der Grundkonfiguration des Systems kann man daher auch oft einstellen, wieviele Handles maximal vergeben werden dürfen. Zu guten alten DOS-Zeiten war das die Einstellung FILES. Bei DOS wurde diese Tabelle auch noch statisch bereitgehalten. Spätere Betriebssytemen haben dann die Tabelle bereits dynamisch geführt; i.d.R. ist sie heute eine verkettete Liste.

        Nun hat man für das advisory Locking nicht die Originaltabelle verändert, sondern einfach eine zweite daneben gebaut, die nur das Handle aus der ersten übernimmt und sich dazu die Lockingstufe merkt und welcher Prozess sie beantragt hat. Du kannst unter PHP ja auch die PID abfragen. Wenn Du  nun ein Script baust, das die PID  und den Status einer Datei (das Ergebnis eines LOCK-Versuches) mit Hilfe von flush() auf dem Client ausgibt und dann noch eine Weile weiterläuft (sleep()) dann kannst Du das Script über ein zweites Fenster Deines Browsers j anochmal aufrufen. Es wird dann eine andere PID haben und dir sicher melden, dass die Datei zur Zeit z.B. EXCLUSIV gesperrt ist, oder nur ein LOCK_SH möglich ist (dann besteht schon ein LOCK_SH eines anderen Prozesses).

        Mach doch einfach mal ein paar solcher Versuche.

        lokale Handletabelle         zentrale Lockingtabelle
                    1 -----------+               1
                    2            |               2
                    3            +----+-->-->--->3   PID  filePtr  openmode SH
                                      +--------->3   PID  filePtr  openmode SH
                                      |  +------>4
                                      |  |       5
                                      |  |      ...
           lokale handletabelle       |  |
                    1-----------------+  |
                    2--------------------+
                    3

        Wenn in der Liste für die Datei (3) schon ein Eitrag besteht mit LOCK_SH, kann kein _anderer_ Prozess einen mit LOCK_EX bekommen. Wenn schon ein Eintrag mit LOCK_EX besteht, kann kein _anderer_ Prozess einen Eintrag bekommen.

        Liebe Grüße aus http://www.braunschweig.de

        Tom

        --
        Fortschritt entsteht nur durch die Auseinandersetzung der Kreativen
        Nur selber lernen macht schlau
      2. Hallo Andreas!

        hoffe ich auch ,-) Das hat mich erstmal erschlagen. Deshalb auch keine Reaktion bis jetzt.

        Sorry wenn ich Dich jetzt etwas verwirrt habe. Das Problem mit einer eindeutigen ID(Counter) ist was recht spezielles, weil sich AFAIK nicht mit einem handle die letzte ID aus einer Datei lesen lässt, und sofort mit der neuen überschreiben. Es hat wahrscheinlich nicht viel mit Deinem Problem zu tun.
        Das heißt man muss mitten im Update-Prozess den LOCK lösen, was zu Inkonsistenzen führen kann.
        Im Normalfall - solange Du sicher stellst dass alle Scripte die auf die Datei zugreifen eben diesen flock() Mechanismus verwenden, hast Du kein Problem.
        Ich weiß nicht genau was Du vorhast, solange Du _nur_ liest oder _nur_ schreibst ist das eben kein Problem, nur wenn man beides "atomar" kombinieren musst, wie beim Counter.

        In meinem Posting unten([pref:t=82711&m=484392]) habe ich eine Lösung für diesen Fall gepostet, und zwar indem man so einen Update-Prozess mit lesen und schreiben (wie beim Counter) mit einem LOCK auf eine externe Datei umschließt. Denn solange die externe Datei gesperrt ist, bekommen andere Prozesse/Scripte(die denselben Mechanismus verwenden) keinen Zugriff auf eben diese Datei und warten an dieser Stelle, und kommen erst zur eigentlichen Update-Operation wenn der vorherige Prozess mit dem Update fertig ist und hinterher den LOCK von der externen Datei entfernt hat.

        Aber wie gesagt, wenn Du sowieso nur ein Handle brauchst, dann mach es so wie es im Manual steht und Du hast kein Problem.

        Ich brauch noch Zeit zum Probieren. Aber eins verstehe ich nicht: Auch in der Doku steht was, daß die Prozesse die gleichen Regeln beachten müssen oder so. Wie stelle ich das denn fest? Also bei mir greifen nicht verschiedene Programme auf eine Datei zu, sondern das eine Script eben von verschiedenen Clients angefordert.

        Ja, das reicht schon. Die Scripte werden z.B. von verschiedenen Apache-Prozessen ausgeführt, auch dieses eine Scripte kann mehr oder wenige gleichzeitig von mehreren Apache-Prozessen ausgeführt werden. Aber da in diesem Fall ja alle den Code mit flock() verwenden, wird es zuverlässig funktionieren. Du könntest die Datei auch in anderen Scripten verwenden, aber musst dann auch entsprechend mit flock() arbeiten, wenn das Locking zuverlässig funktionieren soll.

        Sorry nochmal fürs Verwirren ;-)

        Grüße
        Andreas

        --
        SELFHTML Linkverzeichnis: http://aktuell.de.selfhtml.org/links/
        1. Es hat wahrscheinlich nicht viel mit Deinem Problem zu tun.

          dochdoch durchaus, deshalb fand ich das Beispiel sehr interessant.

          Ich weiß nicht genau was Du vorhast, solange Du _nur_ liest oder _nur_ schreibst ist das eben kein Problem, nur wenn man beides "atomar" kombinieren musst, wie beim Counter.

          Es geht um die neue Version der Zitatesammlung. Wenn man einen Vorschlag macht, wird die XML-Datei ausgelesen, die maxID ermittelt, der neue Eintrag mit einer inkrementierten ID versehen und reingeschrieben. Außerdem kann man abstimmen. Da wird die Votings-Datei augelesen die neue Stimme eingetragen und alles wieder in die Datei zurückgeschrieben. Da kann natürlich auch eine Stimme verloren gehen, wenn zwei Änderungen zur gleichen Zeit _vor_ dem zurückschreiben gemacht werden.

          Denn solange die externe Datei gesperrt ist, bekommen andere Prozesse/Scripte(die denselben Mechanismus verwenden) keinen Zugriff auf eben diese Datei und warten an dieser Stelle

          der Punkt war mir noch wichtig, also der Prozess, der als Zweiter kommt wartet und geht nicht verloren ja?

          Sorry nochmal fürs Verwirren ;-)

          och, das bin ich hier schon gewohnt ;-)

          Gruß, Andreas

          --
          <img src="http://was-ist-das.andreas-lindig.de/was_ist_das_fetzen.jpg" border="0" alt="">
          hier könnte auch ruhig mal'n neues Bild stehen.
          1. Hello,

            der Punkt war mir noch wichtig, also der Prozess, der als Zweiter kommt wartet und geht nicht verloren ja?

            Das ist abhängig vom Lock-Mode.

            wenn Du schreibst

            $lock = flock($fp,LOCK_EX);

            wird das Programm in dieser Funktion solange "angehalten", bis sie erfolgreich war oder max_execution_time erreicht ist. dann wird das Script ja zwangsweise abgebrochen und nur noch der angemeldete Exit-Handler ausgeführt. Alle andere Anweisungen werden ignoriert.

            Da das Timeout meistens auf 30 Sekunden steht (default), kann das für die Benutzer schon mal unangenehm werden. Außerdem wird das System 30 Sekunden lang mit nutzlosen Polls beschäftigt. Die Lockanfragen kommen eher zufällig beim OS an, also werden nicht unbedingt in der Reihenfolge des Eintreffens erledigt. Deshalb können die 30 Sekunden und damit das Timnout schon mal für einen einzelnen Benutzer eintreten, während andere aber schon nach e Sek Erfolg haben.

            Wenn Du den Benutzer nicht quälen willst dann schreib so ungefähr:

            for ($i=1; $i<5; $i++)
              {
                $lock = flock($fp, LOCK_EX + LOCK_NB);
                if($lock) break;         # Bei Erfolg Schleife abbrechen
                usleep(8000);            # 8ms warten, achtung geht nicht bei Win
              }
              if ($lock)
              {
               # weiterarbeiten
              }
              else
              {
               # Fehlerbehandlung. z.B. Meldung, dass die Datei zur Zeit gesperrt ist
               # nicht vergessen, das Handle mit fclose() zurückzugeben [1]
              }

            [1] wenn das _Script_ sowieso terminiert, kann man darauf verzichen, sollte man aber im Interesse einer sauberen Programmierung nicht. Wenn nur die Funktion verlassen wird (return $fehlercode) dann _muss_ man das innerhalb der Funktion beschaffte Handle zurückgeben.

            fLock() besorgt kein Handle, sondern verwendet nur ein durch fopen besorgtes.

            Liebe Grüße aus http://www.braunschweig.de

            Tom

            --
            Fortschritt entsteht nur durch die Auseinandersetzung der Kreativen
            Nur selber lernen macht schlau
          2. Hi Andreas!

            Es geht um die neue Version der Zitatesammlung. Wenn man einen Vorschlag macht, wird die XML-Datei ausgelesen, die maxID ermittelt, der neue Eintrag mit einer inkrementierten ID versehen und reingeschrieben.

            Wie machst Du das denn jetzt, also mit welchen Funktionen arbeitest Du? Ganz grob, wie sieht das öffnen, hinzufügen und schreiben aus?

            Außerdem kann man abstimmen. Da wird die Votings-Datei augelesen die neue Stimme eingetragen und alles wieder in die Datei zurückgeschrieben. Da kann natürlich auch eine Stimme verloren gehen, wenn zwei Änderungen zur gleichen Zeit _vor_ dem zurückschreiben gemacht werden.

            Und wie machst Du das hier?
            Man könnte hier auch die von Tom unten vorgeschlagende Methode anwenden, kommt halt drauf an welche Funktionen Du zum Lesen und Schreiben verwenden willst.

            der Punkt war mir noch wichtig, also der Prozess, der als Zweiter kommt wartet und geht nicht verloren ja?

            Ja.

            Sorry nochmal fürs Verwirren ;-)
            och, das bin ich hier schon gewohnt ;-)

            *g*

            Grüße
            Andreas

            --
            SELFHTML Feature Artikel: http://aktuell.de.selfhtml.org/artikel/
  4. Hallo,

    ich werfe mal bei der Gelegenheit noch ein paar Race Conditions in die Runde: http://www.dclp-faq.de/q/q-race-condition.html. Ob die Verwendung von tempnam() nun vorteilhaft ist oder nicht, mögen andere entscheiden.

    Gruß,
    _Dirk

    1. Hello,

      ich werfe mal bei der Gelegenheit noch ein paar Race Conditions in die Runde: http://www.dclp-faq.de/q/q-race-condition.html. Ob die Verwendung von tempnam() nun vorteilhaft ist oder nicht, mögen andere entscheiden.

      Aus diesem Grunde benutzt man für das Öffnen und/oder Anlegen einer Datei auch den (a+)-Modus.

      Dieser öffnet eine vorhandene Datei zerstörungsfrei und legt sie innerhalb eines Systemaufrufes an, wenn sie noch nicht vorhanden war. Dazwichen passt dann für die PHP-Schicht kein Blatt Papier mehr...

      Man muss dann abe leider den Dateizeiger neu positionieren und ggf. auch an ftruncate() denken.

      Dennis und ich werden das mal in einem ASrtikel zusammenfassen.

      Liebe Grüße aus http://www.braunschweig.de

      Tom

      --
      Fortschritt entsteht nur durch die Auseinandersetzung der Kreativen
      Nur selber lernen macht schlau