Tom: Zähler-Datei springt auf null!

Beitrag lesen

Hello Ihr zwei (und alle die noch dazukommen),

Wie man sieht sind die flocks drin!! Und am Anfang der PHP Datei hab ich auch noch ein ignore_user_abort(true);!

Das mit dem ignore_user_abort(true) muss ich mir doch tatsächlich nochmal zu Gemüte führen. Könnte doch tatsächlich sein, dass das in manchen Scripten notwendig ist.

Zum lock() und zum fopen():

flock() versucht, unter dem Handle der Datei einen Eintrag in der Sperrtabelle zu setzen. Dabei kann ein Exclusive Lock beantragt werden, dann bekommt niemand anderes mehr ein Lock zugeteilt oder es kann ein Shared Lock beantragt werden. Wenn ein Shared Lock erteilt ist, bekommt niemand mehr ein Exclusive Lock, aber jeder kann noch ein Shared Lock erhalten. Wenn dann z.B. der erste "Locker" sich entscheidet, aus seinem Shared Lock ein Exclusive Lock machen zu lassen, hat er Pech gehabt, da ja ein weiteres Shared Lock eingetragen ist.

Nun zum Zeitverhalten:
flock() in der Voreinstellung arbeitet im Blocking-Mode. das bedeutet, dass die Kontrolle solange nicht aus der Funktion flock() zurückkehrt, bis dieses erfolgreich abgeschlossen werden konnte oder ein Timeout (MaxExecutionTime) das Script sowieso abwürgt. MaxExecutionTime ist nun aber nur die reine verbrauchte Rechenzeit. Da die Funktion flock() aber zwischen ihren wiederholten Versuchen, die Datei zu sperren, immer in die idle-Funktion geht, können aus 30sec MaxExecutionTime schon mal 5 Minuten Wartezeit werden. Idle zählt nicht zur Arbeitszeit [und flock() hat keine Gewerkschaft *gg*].

Ich unterstelle mal, daß du die Werte für den zweiten flock() Parameter im Kopf hast, aber sehr leserlich ist es so nicht; und auch die Bedeutung von "w+" ist mir nicht ganz klar - war das nicht für Lesen+Schreiben über den selben Handle?, wozu dann zweimal fopen?

fopen(...,"w+") würde die Datei im zerstörenden Modus zum Schreiben und Lesen öffnen. Man beachte bitte die Reihenfolge! Erst schreiben, _dann_ lesen. Wenn die Datei nicht vorhanden ist, wird sie angelegt.

fopen(...,"r+") würde die Datei im erhaltenden Modus zum Lesen und Schreiben öffnen. Wenn die Datei nicht vorhanden ist, gibts Gemecker.

fopen(...,"a+") würde die Datei im nicht zerstörenden Modus zum Schreiben hinter dem Ende der Datei, also zum Anhängen, und zum Lesen öffnen. Dieser Öffnungsmodus ist also auf allen Systemen, die bei a+ auch die Bewegung des Dateizeigers zulassen, der geeignetste. Sowohl Linux als auch WinDOS lassen das zu. Man muss dann nur vor dem Auslesen den Zeiger mit rewind() oder fseek() auf das 0te (nullte) Byte, also auf den Anfang stellen

$ok = fseek($fh,0,SEEK_SET);

Davon abgesehen, halte ich flock() auch für keine sichere Methode, schon weil es erst nach fopen() aufgerufen wird und PHP die Fkt. laut Manual auch nur simuliert.

Da wird nichts simuliert und auch nicht emuliert *g*

Man kann ein advisory locking durchführen. Das macht flock(). Advisory bedeutet, dass Lock- und Dateioperationen voneinander entkoppelt sind. Die einzige Verbindung ist das Filehandle, dass man sich mit fopen() besorgt, um es dann als Kopie an flock() zu übergeben. Man kann aber auch auf ein inzwischen ungültiges Handle ein Lock setzen. Also Achtung!

Wenn sich aber jedes Programm (jedes Script), dass die Datei nutzen will, an die Locking-Strategie hält, ist das Verfahren sicher.

Hier der entsprechende Code:

$filename = $Logdir."count.txt";

$filehandler = fopen($filename,"r");
flock($filehandler,1);
$content = fread($filehandler, filesize($filename));
flock($filehandler,3);
fclose($filehandler);
$filehandler = fopen($filename,"w+");
flock($filehandler,2);
$content++;
fwrite($filehandler, $content);
flock($filehandler,3);
fclose($filehandler);

function coutner($filename)
{
  $uab = ignore_user_abort(true);   # damit das Script auch zuende läuft

if ($fh = fopen($filename,"a+"))
  {
    if ($lock = flock($fh,LOCK_EX))   # wouldblock funktioniert noch nicht
    {                                 # wir Locken mit Wait und EXCLUSIV
      if($seek = fseek($fh,0,SEEK_SET); an den Anfang der Datei
      {
        $content = fread($filehandler, filesize($filename)); # Datei auslesen
        # Content in Variablen zerlegen
        # Variablen verarbeiten
        # neuen Content aus Variablen zusammenbauen
        # hier:

$content++;

if($seek = fseek($fh,0,SEEK_SET); an den Anfang der Datei
        {
          $write = fwrite($fh,$content);
          if($write)
          {
            ## falls der neue Content kürzer ist als der alte
            $trunc = ftruncate($fh,strlen($content));
          }
        }
      }
      fclose($fh);  ## Datei schließen und Lock aufheben.

ignore_user_abort($uab); ## wieder auf alten Wert zurücksetzen
      return $seek and $write and $trunc;
    }
  }
  else
  {
    ignore_user_abort($uab); ## wieder auf alten Wert zurücksetzen
    return false;
  }
}

So müsste es funktionieren. habs noch nicht degugged.

Die Datei bleibt die ganze Zeit des Vorganges gesperrt. So ist sichergestellt, das zwischen dem Read von Client A und dem Write von Client A der Client B warten muss.

Da Auslesen, Verändern und Zurückschreiben in Sekundenbruchteilen ablaufen, ist das vertretbar.

Bei langwierigeren Contentoperationen muss man sich ggf. ein anderes Verfahren ausdenken. Bei Satzoperationen, wenn also nicht die ganze Datei von der Änderung betroffen ist, benötigt man dann einen Conflict-Counter. Der muss _vor_ jedem Schreibvorgang nochmals gelesen und verglichen werden.

Das Auslesen und Zurückschreiben von Daten muss aber insgesamt durch ein Lock_Ex geklammert bleiben!

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

Tom

--
Fortschritt entsteht nur durch die Auseinandersetzung der Kreativen