12.12. Was sind Race Conditions? Wie kann ich sie vermeiden?Antwort von Frank WiegandWenn 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: |
||
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 |