Hallo,
welche werte soll ich dann flock geben? lesen und schreiben
ich kenne mich nicht aus und will nichts falsch machen mit dem befehl flock.
Locking von Dateien ist eine sehr trickreiche Sache. Die Funktion flock() bietet 3 mögliche Operationen an:
* LOCK_SH: Ein shared lock auf die Datei anlegen. Es können beliebig viele
Prozesse ein shared lock auf eine Datei anlegen - ein shared
lock verhindert *lediglich*, dass ein exclusive lock auf eine
Datei angelegt wird. Ferner kann man kein shared lock anlegen,
falls die Datei gerade mit einem exclusive lock gelockt ist
(wenn etwas nicht geht, wartet flock() immer, bis die Sperre
wieder frei ist)
* LOCK_EX: Ein exclusive lock auf die Datei anlegen. Es kann nur ein
exclusive lock auf eine Datei angelegt werden.
* LOCK_UN: Alle Locks des *aktuelllen* Prozesses entfernen. Im Prinzip
unnötig, wenn Du die Datei direkt nach dem Lesen/Schreiben
wieder schließen willst.
Wie wäre also die Vorgehensweise für das Locking von Dateien?
* Lesende Prozesse öffnen die Datei mit fopen im 'r'-Modus und versuchen
dann mit LOCK_SH die Datei zu sperren. Sobald flock erfolgreich war,
können sie sich sicher sein, dass kein anderer Prozess die Datei
verändert. Da bei fclose() auch alle Locks entfernt werden, ist es
*nicht* notwendig, die Sperre zu entfernen, wenn man fclose () verwendet.
* Schreibende Prozesss öffnen die Datei (dazu später mehr) und versuchen
dann mit LOCK_EX die Datei zu sperren. Sobald flock erfolgreich war,
können sie sich sicher sein, dass die Datei *nur* für sie geöffent ist
und kein anderer Prozess dazwischenfunkt. LOCK_EX sollte man nur so kurz
wie möglich verwenden, denn solange sind andere Prozesse gesperrt.
Dabei gibt es jedoch noch einige *sehr* wichtige Punkt zu beachten:
* flock() funktioniert nicht auf FAT-Dateisystemen, nicht unter Windows
95/98/ME (unter NT4/2000/XP/2003/Vista schon) und nicht über die meisten
Netzwerkdateisysteme.
* Insbesondere unter Windows, wenn PHP als ISAP-Filter betrieben wird,
aber unter Umständen auch unter anderen Betriebsystemen, wenn PHP als
Multithread-Webservermodul verwendet wird, kann es sein, dass flock()
nutzlos ist, denn flock() ist in manchen Betriebsystemen nur auf
Prozess-, nicht aber auf Thread-Ebene implementiert. Wenn PHP als CGI
oder als Webservermodul eines Singlethread-Webservers läuft, dann
besteht das Problem nicht.
* flock() ist eine *freiwillige* Angelegenheit. Ich kann alles mit der
Datei anstellen, auch wenn sie von einem anderen Prozess per LOCK_EX
gesperrt ist. D.h. alle Programme, die auf die Datei zugreifen, *müssen*
Locking korrekt implementieren, damit es zu keinen Fehlern kommt, d.h.
sie *müssen* sich an die Konvention halten.
Und als _allerwichtigsten_ Punkt kommt noch das Öffnen von Dateien zum Schreiben (!) hinzu. Betrachten wir nun folgenden Beispielcode, der eine Datei zum *Lesen* öffnet:
$fp = fopen("datei", "r");
if (flock($fp, LOCK_SH)) {
// irgendwas aus $fp auslesen
} else {
echo "Fehler beim Anfordern der Sperre!";
}
fclose($fp);
Der Code ist so korrekt, d.h. wenn sich alle lesenden Prozesse an das obige Schema halten, dann funktioniert's korrekt. Betrachten wir jedoch mal folgenden Beispielcode, der eine Datei zum Schreiben öffnet.
$fp = fopen("datei", "w");
if (flock($fp, LOCK_EX)) {
// irgendwas in $fp schreiben
} else {
echo "Fehler beim Anfordern der Sperre!";
}
fclose($fp);
Sieht doch ganz OK aus, oder? FALSCH!
Das Problem an dem obigen Code ist, dass der Code Änderung an der Datei vornimmt, *bevor* das Lock angefordert wurde. Warum das? Da ist doch ne if-Bedingung? Die Antwort: Beim fopen () mit dem "w"-Flag wird der komplette Inhalt der Datei gelöscht (d.h. wenn Du nur fclose (fopen ("datei", "w")); machst, dann löscht Du den kompletten Inhalt der Datei "datei" - oder legst eine Datei mit diesem Namen an).
Oftmals wird hierfür eine Lösung über eine separate Lockfile vorgeschlagen. Obwohl das durchaus funktioniert, ist das nicht die einfachste:
Es gibt zwei sich sehr ähnliche Lösungsmöglichkeiten für das Problem:
1. Die Datei als 'r+' öffnen (statt als 'w') - dann wird die Datei zum Lesen geöffent mit zusätzlicher Möglichkeit, in der Datei zu schreiben - die Datei wird allerdings *nicht* beim Öffnen gelöscht. Nach dem Erlangen der Sperre wird die Datei per ftruncate() gelöscht und dann kann normal darin geschrieben werden:
$fp = fopen("datei", "r+");
if (flock($fp, LOCK_EX)) {
// Kompletten Dateiinhalt löschen
ftruncate ($fp, 0);
// irgendwas in $fp schreiben
} else {
echo "Fehler beim Anfordern der Sperre!";
}
fclose($fp);
Dieses Verfahren hat einen gravierenden Nachteil: Wenn die Datei nicht existiert, schlägt das Öffnen der Datei fehl. Man kann dem aber abhelfen, indem man mit der touch()-Funktion vorher dafür sorgt, dass die Datei existiert, d.h. *vor* dem fopen() noch ein
touch ($datei);
Das sorgt dafür, dass die Datei definitiv existiert, bevor man sie öffnet, allerdings fasst touch() den Inhalt der Datei nicht an.
2. Die alternative Lösung ist, die Datei zum Anhängen zu öffnen ('a' statt 'r+') und dann wieder ftruncate() anzuwenden:
$fp = fopen("datei", "a");
if (flock($fp, LOCK_EX)) {
// Kompletten Dateiinhalt löschen
ftruncate ($fp, 0);
// irgendwas in $fp schreiben
} else {
echo "Fehler beim Anfordern der Sperre!";
}
fclose($fp);
Diese Lösung hat den Vorteil, dass fopen() beim Öffnen über 'a' die Datei im Zweifel auch anlegt. *Allerdings* hat dieses Verfahren den Nachteil, dass man weder im 'a'- noch im 'a+'-Modus in der Datei mittels fseek() die Position verändern kann. Ok, im 'a+'-Modus geht's, um irgendwo aus der Datei etwas zu *lesen* - allerdings wird in beiden 'a'-Modi alles, was geschrieben wird, *immer* an die Datei angehöngt, *nicht* an die aktuelle Position geschrieben - nur das ftruncate() sorgt dafür, es überhaupt wieder am Anfang der Datei losgeht (wenn die Datei leer ist, dann heißt "anhängen" einfach "ab dem Anfang schreiben"). Wenn Du also *nur* linear Schreiben willst und fseek() gar nicht zum Verändern der SCHREIB-Position verwenden willst, dann ist die 'a'-Lösung genauso gut wie die 'r+'-Lösung - wenn Du allerdings mittels fseek() die SCHREIB-Position verändern willst, dann ist die 'a'-Lösung keine Option und Du musst die 'r+'-Lösung verwenden.
Viele Grüße,
Christian
"I have always wished for my computer to be as easy to use as my telephone; my wish has come true because I can no longer figure out how to use my telephone." - Bjarne Stroustrup