de.comp.lang.php.* FAQ

12.12. Was sind Race Conditions? Wie kann ich sie vermeiden?

Antwort von Frank Wiegand

Wenn das Ergebnis mehrerer Ereignisse von deren Reihenfolge abhängt, und diese Reihenfolge zeitlich nicht garantiert werden kann, dann spricht man von einer Race Condition. Der Name "Race Condition" leitet sich davon ab, dass mehrere Prozesse eine Art Rennen um die betroffene Ressource austragen, welches nur einer gewinnen kann.

Ein typisches Beispiel für eine Race Condition:

<?php

  $datei = '/home/frank/datei';

  // wenn $datei nicht existiert ...
  if (!file_exists ($datei)) {
    // ... dann $datei zum Schreiben öffnen
    $fp = fopen ($datei, 'w');

    // andere Zugriffe abblocken
    flock ($fp, LOCK_EX);
    
    // Daten in die Datei schreiben
    fputs ($fp, $data);

    // Datei freigeben
    flock ($fp, LOCK_UN);
  }
  else {
    // Datei existiert bereits
  }
  
?>

Nachdem der Test mit file_exists() von dem Script (Prozess 1) durchgeführt worden ist, kann es sein, dass der Prozessor zunächst einem anderen Prozess (Prozess 2) Rechenzeit gewährt. Der Vorgang Test auf Existenz und Lockfile anlegen läuft nicht atomar, sondern geteilt ab. Prozess 2 kann in der Zwischenzeit die Datei $datei als symbolischen Link auf eine andere Datei anlegen. Das ist besonders gefährlich, da Prozess 2 beispielsweise nicht unbedingt Schreibrechte an der gelinkten Datei haben muss. Wenn Prozess 1 wieder an der Reihe ist, wird die von Prozess 2 verlinkte Datei zum Schreiben geöffnet. Ein Eindringling kann dies ausnutzen und mithilfe von Prozess 1 wichtige Daten überschreiben lassen. Oft hört man das Argument, dass dieser Fall äußerst unwahrscheinlich ist. Das stimmt nicht - oft reicht es schon aus, die entsprechenden Programme häufig (mehrere Male pro Sekunde) auszuführen. Es ist nur eine Frage der Zeit, wann die Race Condition auftritt.

Die Race Condition wird vermieden, indem man die Operation des Lockfile anlegen atomar, d. h. unteilbar ausführt. Dazu werden die Daten zunächst in eine temporäre Datei geschrieben. In PHP gibt es die Funktion tempnam() , welche eine temporäre Datei mit eindeutigem Namen anlegt. Anschliessend erstellt man die gewünschte mit der Funktion link() an der entsprechenden Stelle. link() ist eine atomare Funktion und gibt false zurück, wenn die Datei nicht erstellt werden konnte. Dabei ist zu beachten, dass link() nicht über Dateisysteme hinweg funktioniert. Die temporäre Datei muss also im selben Dateisystem (auf derselben Partition) angelegt werden.

Hier nun das Beispiel ohne Race Condition:

<?php
  
  $datei = '/home/frank/datei';
  
  // temporäre Datei anlegen
  $tempnam = tempnam ('/home/frank/tmp', 'foo');

  // Fehler: Datei konnte nicht angelegt werden
  if ($tempnam === false)
    die ("Could not create $tempnam!");
  
  // temporäre Datei zum Schreiben öffnen
  $fp = fopen ($tempnam, 'w') or die ("Could not open $tempnam!");

  // andere Zugriffe abblocken
  flock ($fp, LOCK_EX);

  // Daten in die Datei schreiben
  fputs ($fp, $data);

  // Datei freigeben
  flock ($fp, LOCK_UN);
  fclose ($fp);

  // $datei zu temporärer Datei hardlinken:
  // schlägt fehl, wenn $datei schon vorhanden ist
  if (!link ($tempnam, $datei)) {
    // Fehlschlag: Datei bereits vorhanden
  }

  // temporäre Datei löschen
  unlink ($tempnam);

?>

Unter Windows existiert die Funktion link() nicht. Stattdessen kann man rename() verwenden. Mit der Verwendung von rename() entfällt auch das Löschen der temporären Datei. rename() unter Windows überschreibt nicht vorhandene Dateien, während rename() unter UNIX/Linux dies tut.

Allgemein gilt also: Atomare Operationen wie z. B. Operationen auf Dateien müssen atomar (d. h. in einem Schritt) ausgeführt werden. Viele Funktionen geben einen booleschen Wert zurück, mit dem man überprüfen kann, ob die Operation erfolgreich war.

Diese Funktionen sind ein Anzeichen dafür, dass man eventuell eine Race Condition programmiert hat:

Valid HTML 4.01! Valid CSS!

12.12. Was sind Race Conditions? Wie kann ich sie vermeiden?
http://www.php-faq.de/q/q-race-condition.html
Archiv der de.comp.lang.php-FAQ Dies ist eine Archivseite von 2008 und wurde seitdem nicht geändert. Das dclp-FAQ-Team