Neuer Artikel: Sperren von Dateien
Christian Seiler
- zur info
0 Der Martin0 Schuer0 Horst3 Horst
0 Tom
Hallo allerseits,
Ich habe einen neuen Artikel geschrieben: Sperren von Dateien.
Siehe auch den zugehörigen Weblog-Eintrag.
Viele Grüße,
Christian
Hallo Christian,
Ich habe einen neuen Artikel geschrieben: Sperren von Dateien.
prima, das ist ein Thema, das immer wieder auftaucht und dessen Wichtigkeit regelmäßig unterschätzt wird.
Schade jedoch, dass du den Artikel in so viele Einzelseiten zerteilt hast; das macht ihn meiner Ansicht nach unübersichtlich und etwas unhandlich.
Unter Windows kann eine Datei übrigens nicht nur über LockFileEx() gesperrt werden, sondern auch über den ShareMode-Parameter der Funktion CreateFile(). Das ist dann sogar unabhängig vom Filesystem und funktioniert nicht nur auf NTFS, sondern auch auf FAT-Volumes.
Schönes Wochenende noch,
Martin
Hallo Martin,
Schade jedoch, dass du den Artikel in so viele Einzelseiten zerteilt hast; das macht ihn meiner Ansicht nach unübersichtlich und etwas unhandlich.
*LOL* - ich hatte das ursprünglich als eine große Seite und dann hat Siechfred gemeint, die wäre ein Monstrum, in dem man sich nicht zurechtfinden würde, also hab ich ihn aufgeteilt. Man kann's wohl keinem Recht machen. ;-)
Unter Windows kann eine Datei übrigens nicht nur über LockFileEx() gesperrt werden, sondern auch über den ShareMode-Parameter der Funktion CreateFile(). Das ist dann sogar unabhängig vom Filesystem und funktioniert nicht nur auf NTFS, sondern auch auf FAT-Volumes.
Ja, ich weiß - wobei ShareMode etwas leicht anderes ist, als "klassisches" Locking (und ich auch gerade nicht auswendig weiß, was da bei Netzwerkdateisystemen passiert, wenn unterschiedliche Rechner das machen wollen). Das Problem an ShareMode ist, dass alle 4 hier vorgestellten Programmiersprachen das in der Form nicht unterstützen (zumindest nicht ohne Extra-Module) und - weil's eben direkt bei Open angegeben werden muss - nicht portabel ist.
Zudem: Ich denke jeder, der ernsthaft einen Windows-Server betreibt, hat heutzutage NTFS, daher halte ich das ganze für kein wirkliches Problem.
Viele Grüße,
Christian
Hallo,
Schade jedoch, dass du den Artikel in so viele Einzelseiten zerteilt hast
Man kann's wohl keinem Recht machen. ;-)
das ist natürlich wahr, ich hatte auch nur meinen Eindruck wiedergegeben. Ich habe größere Informationsmengen halt lieber am Stück, das ist einfach praktischer in der Handhabung (speichern, drucken, in andere Formate umsetzen).
Unter Windows kann eine Datei übrigens nicht nur über LockFileEx() gesperrt werden, sondern auch über den ShareMode-Parameter der Funktion CreateFile().
Ja, ich weiß
Gut. Dein Artikel las sich so, als würdest du behaupten, dass LockFileEx() die einzige Möglichkeit sei.
(und ich auch gerade nicht auswendig weiß, was da bei Netzwerkdateisystemen passiert, wenn unterschiedliche Rechner das machen wollen).
Nichts. Jedenfalls aus der Sicht des Windows-Rechners. Bei Netzwerkvolumes überlässt Windows das Locking komplett dem Host, dem das Dateisystem gehört. Ist das bei den anderen nicht auch so? Kann ein Linux-Host von sich aus eine Datei für den Zugriff sperren, die auf einem anderen Host liegt? Oder macht das nicht in Wirklichkeit auch der Server, der die Datei zur Verfügung stellt?
Zudem: Ich denke jeder, der ernsthaft einen Windows-Server betreibt, hat heutzutage NTFS
Da hast du wahrscheinlich Recht, aber die Problematik des File-Lockings besteht ja nicht nur in Server-Umgebungen, sondern auch bei lokalen Anwendungen, die konkurrierend auf dieselbe Datei zugreifen wollen (oder sogar konkurrierende Threads innerhalb einer Anwendung). Daher ist das Thema sogar für die Nur-Windows-Programmierer interessant.
So long,
Martin
Hallo Martin,
(und ich auch gerade nicht auswendig weiß, was da bei Netzwerkdateisystemen passiert, wenn unterschiedliche Rechner das machen wollen).
Nichts. Jedenfalls aus der Sicht des Windows-Rechners. Bei Netzwerkvolumes überlässt Windows das Locking komplett dem Host, dem das Dateisystem gehört. Ist das bei den anderen nicht auch so? Kann ein Linux-Host von sich aus eine Datei für den Zugriff sperren, die auf einem anderen Host liegt? Oder macht das nicht in Wirklichkeit auch der Server, der die Datei zur Verfügung stellt?
Ich verstehe nicht ganz, was Du jetzt wie meinst.
Folgende Situation (egal ob jetzt Linux oder Windows und ob jetzt SMB oder NFS): Rechner A ist der "Server", Rechner B und C sind die "Clients" (im SMB/NFS-Sinn). Sprich: Rechner A gibt irgend ein Verzeichnis frei, das auf Rechnern B und C eingebunden ist.
Nun sperrt ein Programm auf Rechner B eine Datei, die eigentlich auf Rechner A liegt. Was passiert, wenn ein Programm auf Rechner C nun die selbe Datei sperren will?
Im folgenden werde ich folgende Begriffe nutzen:
"Funktioniert" == Rechner C muss warten
"Funktionier nicht" == Rechner C denkt, er habe die Datei gesperrt
(d.h. die Sperren sind unabhängig voneinander nur
auf den jeweiligen Rechnern vorhanden und daher
kann es zu Konflikten Kommen)
"Fehler" == auf Rechner C tritt eine Fehlermeldung auf
[Den Fall, in denen gar keine Rechner überhaupt Dateien sperren können, weil Funktionalität deaktiviert oder ähnliches, betrachte ich hier mal nicht.]
Was passiert nun?
* Windows/LockFileEx: "Funktioniert"
* Linux/flock(2) (und vmtl. auch FreeBSD/flock(2) und MacOSX/flock(2):
"Funktioniert nicht"
* Linux/fcntl(2) (und vmtl. auch FreeBSD/fcntl(2) und MacOSX/fcntl(2):
"Funktioniert"
Meine Klammer bezog sich nun darauf, dass ich weder recherchiert noch getestet habe, was unter Windows mit den Share-Modes passiert, wenn diese über das Netzwerk verwendet werden. Wäre also nett, wenn Du sagen kannst, in welchen der drei obigen Fälle folgendes Szenario passt:
Rechner A, B und C sind Windows-Rechner (2000, 2003 oder XP z.B.). Auf Rechner B öffnet ein Prozess eine Datei auf Rechner A mit ShareMode == "ich teile gar nichts" und kurz darauf während die Datei noch offen ist versucht ein Prozess auf Rechner C die selbe Datei auch mit ShareMode == "ich teile gar nichts" zu öffnen. Was passiert hier?
Zudem: Ich denke jeder, der ernsthaft einen Windows-Server betreibt, hat heutzutage NTFS
Da hast du wahrscheinlich Recht, aber die Problematik des File-Lockings besteht ja nicht nur in Server-Umgebungen, sondern auch bei lokalen Anwendungen, die konkurrierend auf dieselbe Datei zugreifen wollen (oder sogar konkurrierende Threads innerhalb einer Anwendung). Daher ist das Thema sogar für die Nur-Windows-Programmierer interessant.
Ja klar. Nur wenn das meine Zielgruppe gewesen wäre, dann hätte ich einen Abschnitt über C hinzugefügt, der die Windows-API-Funktion selbst erläutert. ;-) Und dann hätte ich CreateFile auch besprochen - und die damit verbundenen ShareModes.
Viele Grüße,
Christian
Hallo Christian,
Ich verstehe nicht ganz, was Du jetzt wie meinst.
und dabei ist es doch gar nicht so kompliziert. ;-)
Folgende Situation (egal ob jetzt Linux oder Windows und ob jetzt SMB oder NFS): Rechner A ist der "Server", Rechner B und C sind die "Clients" (im SMB/NFS-Sinn). Sprich: Rechner A gibt irgend ein Verzeichnis frei, das auf Rechnern B und C eingebunden ist.
Klar soweit.
Nun sperrt ein Programm auf Rechner B eine Datei, die eigentlich auf Rechner A liegt. Was passiert, wenn ein Programm auf Rechner C nun die selbe Datei sperren will?
Das Ergebnis ist dasselbe, als wenn Rechner A, B und C identisch wären, die Datei also auf derselben Maschine liegt wie die beiden konkurrierenden Client-Applikationen.
[Den Fall, in denen gar keine Rechner überhaupt Dateien sperren können, weil Funktionalität deaktiviert oder ähnliches, betrachte ich hier mal nicht.]
Logisch. :-)
Rechner A, B und C sind Windows-Rechner (2000, 2003 oder XP z.B.). Auf Rechner B öffnet ein Prozess eine Datei auf Rechner A mit ShareMode == "ich teile gar nichts" und kurz darauf während die Datei noch offen ist versucht ein Prozess auf Rechner C die selbe Datei auch mit ShareMode == "ich teile gar nichts" zu öffnen. Was passiert hier?
Unter Windows bekommt Rechner/Prozess C eine Fehlermeldung (Sharing Violation) und muss den Zugriff selbständig wiederholen. Ein "Warte bis Datei freigegeben" gibt es unter Windows AFAIK nicht, wenn es nicht auf Anwendungsebene implementiert wird.
Ich wollte nur darauf hinaus, dass in deinem Szenario nicht die Clients B und C die Locks verwalten, sondern logischerweise der Server A. Die beiden Clients "reichen nur durch".
Selbst wenn auf Rechner B noch zwei Applikationen um den Zugriff konkurrieren würden und das OS (Windows) auf Rechner B theoretisch schon einen Konflikt feststellen könnte, reicht es beide Zugriffe an Rechner A weiter und lässt ihn den Konflikt feststellen.
Nur wenn das meine Zielgruppe gewesen wäre, dann hätte ich einen Abschnitt über C hinzugefügt
Den habe ich irgendwie vermisst. Denn der Artikel ist mit "Sperren von Dateien" überschrieben, auch die Einleitung ist zunächst allgemein gehalten. Dann gehst du auf vier sehr konkrete, spezielle Sprachen ein, ohne die Problematik zunächst allgemein sprachunabhängig zu beleuchten.
Ich meine, das ist völlig legitim so. Aber dann sollte der Titel vielleicht schon auf die spezielle Ausrichtung auf Web-typische Sprachen hinweisen.
Schönen Abend noch,
Martin
Hello,
Unter Windows bekommt Rechner/Prozess C eine Fehlermeldung (Sharing Violation) und muss den Zugriff selbständig wiederholen. Ein "Warte bis Datei freigegeben" gibt es unter Windows AFAIK nicht, wenn es nicht auf Anwendungsebene implementiert wird.
Ich weiß nur noch auswendig, wie es bei Windows 98 war, bzw beim Zusammenspiel von DOS und NOVELL.
Dos bekam durch die Netzwerkshell eine Erweiterung des Int 21h für die Netzwerkzugriffe. Diese wurden dann über die Shell an den Server gesandt und dort umgestezt. Ein Warten gab es nicht.
"Filemode 2" war der alte Öffnungsmode für rdwr-lock
Durch setzen des share-flags konnte man dann sagen, ich teile die Datei mit anderen und durch setzen der share bzw exclusive-Flags konnte man dann genau das erreichen, was man heute auch erreichen kann.
Und ein bisschen alten Code für eine eigene NOVELL-API auf DOS habe ich auch noch gefunden ;-)
Es gab also beim Record-Locking eine Wartezeit, die man in Ticks vorgeben konnte.
Hier ein kleiner Ausschnitt...
Function NewRecLock(Var DataBase : File;
Pos : Longint;
Len : Longint;
LOCK : Boolean;
EXCL : Boolean;
TIMEOUT : Word ) :Word;
var
Regs : Registers;
Flags : Byte;
begin
Regs.AH := $BC; { Funktion Log Physical Record }
Flags := 0;
If Lock then Flags := (Flags or 1);
If not EXCL then Flags := (Flags or 2); { Bit 1 setzen für Non-Excl }
Regs.AL := Flags;
Regs.BX := FileRec(Database).Handle;
Regs.CX := DWord(Pos).HiWord; { Position des Records im File }
Regs.DX := DWord(Pos).LoWord;
Regs.SI := DWord(Len).HiWord; { Länge des Records }
Regs.DI := DWord(Len).LoWord;
Regs.BP := TimeOut; { Wartezeit in Ticks, für Lockversuch }
MsDos(Regs);
case Regs.AL of
0: NewRecLock := 0;
$96: NewRecLock := 90; { zu wenig dynamischer Speicher im Server }
$FE: NewRecLock := 91; { Time-Out-Fehler }
$FF: NewRecLock := 92; { Sperrversuch abgewiesesen, "FAILED" }
else NewRecLock := 59; { Nichterkundbarer Netzwerkfehler }
end; { Case Regs.AL }
end;
Wenn ich noch ein bisschen weiterkrame, finde ich die Files für Windows (bis NT 4.0) sicher auch noch.
Harzliche Grüße vom Berg
http://bergpost.annerschbarrich.de
Tom
Hallo Martin,
Unter Windows bekommt Rechner/Prozess C eine Fehlermeldung (Sharing Violation) und muss den Zugriff selbständig wiederholen.
Also der Fall "Fehler" bei meiner obigen Ausführung. Das macht die Sharing-Methode zumindest sehr umständlich zu benutzen, da man das "ich probiere es selbst wieder" implementieren muss.
Ein "Warte bis Datei freigegeben" gibt es unter Windows AFAIK nicht, wenn es nicht auf Anwendungsebene implementiert wird.
Doch: Bei LockFileEx wird (bei entsprechend gesetzten Parametern) darauf gewartet, bis das Lock wieder frei wird, bevor die Funktion zurückkehrt.
Viele Grüße,
Christian
Ich habe einen neuen Artikel geschrieben: Sperren von Dateien.
Großartig, vielen Dank!
Viele Grüße!
_ds
Hallo,
Ich habe einen neuen Artikel geschrieben: Sperren von Dateien.
Ich sag einfach mal Danke!
Viele Grüße,
Hotte
, schreibt auch grade einen Artikel...
Viele Grüße,
Hotte, schreibt auch grade einen Artikel...
Hier isser:
http://rolfrost.de/control.html
Viel Spass damit und viele Grüße,
Horst Haselhuhn
Hello,
Ich habe einen neuen Artikel geschrieben: Sperren von Dateien.
Sehr lobenswert!
Anmerkung zum PHP-Teil:
if (!flock ($fp, LOCK_EX)) {
die ('Sperren der Datei fehlgeschlagen!');
Bist Du sicher, dass das die überhaupt noch ausgeführt wird?
MMn wartet das flock() solange, bis es Erfolg hat, oder aber das PHP-Timeout zuschlägt.
Und _wenn_ die() hier jemals ausgeführt würde, wäre es nicht unbedingt geschickt, ein größeres Skript derartig abzubrechen. Es sollte dann eine qualifizierte Fehlerbehandlung stattfinden, die den User nicht in den Wald stellt.
Leider sind solche ja durchaus "funzenden" Beispiele geeignet, sich über den Weg des Copy & Paste beliebig oft zu vervielfältigen und sich dann als Standart (sic!) zu etablieren.
Wenn Du es also nicht als allzu unverschämt empfindest, würde ich an diesen Stellen gerne "nachrüsten" und zumindest vernünftige Funktionen mit qualifizierter Fehlerbehandlung dafür vorschlagen.
Ein paar Grafiken würden die Sache sicher noch verständlicher machen, aber dafür bin ich leider nicht der passende Künstler.
Noch eine Frage zu touch(), und den anderen namensbasierten Dateifunktionen:
Hat schonmal jemand ausprobiert, was die machen, wenn es auf ein Advisory Lock laufen?
Eigentlich sollte sowas im PHP-Manual dokumentiert sein, aber ich konnte bisher nie etwas finden zu dem Thema. Nur bei file_put_contents() http://de2.php.net/manual/de/function.file-put-contents.php ist da inzwischen 'was eingebaut.
Harzliche Grüße vom Berg
http://bergpost.annerschbarrich.de
Tom
Hallo,
Anmerkung zum PHP-Teil:
if (!flock ($fp, LOCK_EX)) {
die ('Sperren der Datei fehlgeschlagen!');Bist Du sicher, dass das die überhaupt noch ausgeführt wird?
MMn wartet das flock() solange, bis es Erfolg hat, oder aber das PHP-Timeout zuschlägt.
Nein, das ist vollkommen falsch. flock() wartet grundsätzlich, bis es Erfolg hat. Das PHP-Timeout (Du meinst set_time_limit() bzw. die entsprechende ini-Einstellung, nehme ich an?) schlägt bei Systemaufrufen nämlich in KEINEM Fall zu! Die einzige Möglichkeit, flock() zu unterbrechen, ist ein nicht-ignoriertes Signal an den Prozess zu schicken. Aber solange man kein Deadlock produziert, ist es eigentlich nie ein Problem, auf eine Sperre zu warten, denn irgendwann wird die schon wieder frei werden.
Also: Es gibt 3 Möglichkeiten:
a) Der Aufruf kehrt zurück und die Sperre wurde etabliert
b) Der Aufruf kehrt nicht zurück und der Prozess wartet ewig
(falls eben ein Deadlock auftritt)
c) Der Aufruf kehrt zurück und ein Fehler beim Sperren ist aufgetreten.
Dies kann zum Beispiel passieren, wenn man fopen-Wrapper verwendet
und versucht, eine Datei über FTP zu öffnen - da funktioniert das
Sperren nämlich gar nicht (FTP kann so etwas nicht). Oder es kann
passieren, wenn man's unter Windows 98 versucht.
Und genau den Fall (c) soll die die()-Abfrage abfangen. Nicht mehr, nicht weniger.
Und _wenn_ die() hier jemals ausgeführt würde, wäre es nicht unbedingt geschickt, ein größeres Skript derartig abzubrechen. Es sollte dann eine qualifizierte Fehlerbehandlung stattfinden, die den User nicht in den Wald stellt.
Natürlich sollte in größeren Scripten bessere Fehlerbehandlungsmöglichkeiten existieren, aber dies ist explizit ein einfaches Beispiel. Wer ein größeres Script schreibt und sich bereits Gedanken um Fehlerbehandlung gemacht hat, wird ja wohl selbst noch erkennen, dass die() in dem Kontext keine gute Wahl ist und das von selbst ersetzen.
Ich wollte das Beispiel möglichst einfach halten, so dass wirklich nur der eigentliche Sachverhalt dargestellt wird - und bei diesen einfachen Beispielen, die nichts anderes tun, funktioniert die Fehlerbehandlung über die() zufriedenstellend. Wenn ich dort jetzt noch eine ausgereifte Fehlerbehandlung einbauen würde, dann würde ich das Beispiel nur unnötig verkomplizieren. Zumal es bei ausgereifter Fehlerbehandlung wieder unterschiedliche Geschmäcker gibt: Exceptions, bestimmte Rückgabewerte, einen User-Error mit PHP werfen, usw. usf. Sorry, aber das ist schlichtweg nicht das Thema des Artikels.
Leider sind solche ja durchaus "funzenden" Beispiele geeignet, sich über den Weg des Copy & Paste beliebig oft zu vervielfältigen und sich dann als Standart (sic!) zu etablieren.
Ich habe kein Problem damit, wenn sich der aktuelle Code als Standard etablieren würde, denn ich kann Deine Kritik nicht nachvollziehen (s.o.).
Noch eine Frage zu touch(), und den anderen namensbasierten Dateifunktionen:
Hat schonmal jemand ausprobiert, was die machen, wenn es auf ein Advisory Lock laufen?
Sie funktionieren weiterhin. Das musst Du auch nicht ausprobieren, denn (und das steht übrigens auch in meinem Artikel drin) ein Advisory Lock ist eben "freiwillig", d.h. bei Advisory Locking kann ein Programm, das den Locking-Mechanismus nicht nutzt, beliebiges mit der Datei anstellen.
Und selbst bei Mandatory Locks unter Windows ist touch() kein Problem, da es ja explizit nichts in die Datei schreibt oder daraus liest - und das reine Öffnen (solange man die Datei nicht dabei zerstört) ist weiterhin problemlos erlaubt - auch bei Mandatory Locking. file_put_contents() oder ähnliches wird halt bei Mandatory Locking versagen.
Viele Grüße,
Christian
Hallo nochmal,
file_put_contents() oder ähnliches wird halt bei Mandatory Locking versagen.
Oh, das hatte ich überlesen, das hat das wohl inzwischen Locks eingebaut.
Allerdings habe ich mir gerade den Sourcecode angesehen und schwupp... die PHP-Leute haben's natürlich falsch gemacht. In PHP 5.2.4 steht nämlich (ext/standard/file.c):
stream = php_stream_open_wrapper_ex(filename, (flags & PHP_FILE_APPEND) ? "ab" : "wb",
((flags & PHP_FILE_USE_INCLUDE_PATH) ? USE_PATH : 0) | ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL, context);
if (stream == NULL) {
RETURN_FALSE;
}
if (flags & LOCK_EX && php_stream_lock(stream, LOCK_EX)) {
php_stream_close(stream);
RETURN_FALSE;
}
Das heißt, sie machen gerade "Dateien zum Schreiben öffnen" falsch. Mal Testcase schreiben und als Bug melden.
Also ist meine Aussage, dass es bei Mandatory Locking versagen wird, wohl doch nicht ganz falsch - obwohl's inzwischen "im Prinzip" nicht versagen sollte...
Viele Grüße,
Christian
Hallo,
[PHP: file_put_contents + LOCK_EX]
Mal Testcase schreiben und als Bug melden.
Zur Info: Ich habe den Bug bei PHP mal gemeldet.
http://bugs.php.net/bug.php?id=43182
Viele Grüße,
Christian
Hello,
if (!flock ($fp, LOCK_EX)) {
die ('Sperren der Datei fehlgeschlagen!');
}
Also: Es gibt 3 Möglichkeiten:
c) Der Aufruf kehrt zurück und ein Fehler beim Sperren ist aufgetreten.
Dies kann zum Beispiel passieren, wenn man fopen-Wrapper verwendet
und versucht, eine Datei über FTP zu öffnen - da funktioniert das
Sperren nämlich gar nicht (FTP kann so etwas nicht). Oder es kann
passieren, wenn man's unter Windows 98 versucht.
Die Möglichkeit habe ich eben nicht bedacht, das stimmt.
Meine Überlegung war "Wenn fopen() versagt (die Datei nicht da ist), gehe ich gar nicht zum flock()".
Aber das ist eben nicht zuende gedacht, weil die Datei zwischen dem erfolgreichen fopen() und dem flock() auch schon wieder verschwunden sein könnte oder sonstiges...
Bleibt noch eine Frage: Was macht flock( ... LOCK_EX + LOCK_NB), wenn bereits ein dio_lock gesetzt ist? Theoretisch könnte es ja sagen: Lockanfoerderung nicht möglich, aber ich glaube, es wartet trotzdem (so war es bei PHP4).
Kannst Du das ausprobieren oder besser auch nachschauen?
Harzliche Grüße vom Berg
http://bergpost.annerschbarrich.de
Tom
Hallo,
Aber das ist eben nicht zuende gedacht, weil die Datei zwischen dem erfolgreichen fopen() und dem flock() auch schon wieder verschwunden sein könnte oder sonstiges...
Wenn der Dateiname weg ist, versagt flock() deswegen trotzdem nicht (unter UNIX-artigen Betriebssystemen), sondern sperrt die Datei weiterhin (unter UNIX-artigen Betriebssystemen sind Dateien nicht zwangsläufig an Namen gebunden!) und unter Windows kannst Du eine Datei, die geöffnet wurde, nicht löschen.
Bleibt noch eine Frage: Was macht flock( ... LOCK_EX + LOCK_NB), wenn bereits ein dio_lock gesetzt ist?
Es bekommt die Sperre. Das würde auch nur mit LOCK_EX gehen. Wie auch in meinem Artikel erwähnt ist, nutzt dio_fcntl die Funktion fcntl(2) während flock in der Regel flock(2) nutzt, die aber vollkommen unabhängig voneinander sind.
Theoretisch könnte es ja sagen: Lockanfoerderung nicht möglich, aber ich glaube, es wartet trotzdem (so war es bei PHP4).
Wenn Du NB setzt, wartet gar nichts. Definitiv nihct. Und die Sperre ist möglich, weil's eine komplett verschiede Sperre ist, als die andere Funktion. Deswegen muss man auch beim programmiersprachenübergreifenden Sperren von Dateien höllisch aufpassen.
Kannst Du das ausprobieren oder besser auch nachschauen?
Ich muss weder noch - ich hab's ja genau so, wie ich in diesem Posting noch einmal ausgeführt habe, erst in den Artikel geschrieben (vgl. die Abschnitte über die Betriebsysteminterna mit dem PHP-Abschnitt mit dem Abschnitt über programmiersprachenübergreifende Sperren). Mich beschleicht langsam das Gefühl, Du hättest den nicht vollständig gelesen, sondern nur überflogen...
Viele Grüße,
Christian
Hello,
Kannst Du das ausprobieren oder besser auch nachschauen?
Ich muss weder noch
Es war auch nur eine Bitte.
(Ich kann es z.Zt. nur mit PHP4 ausprobieren, und das ist ja witzlos, denn da wurde
definitv totzdem gewartet, auch wenn extra NonBlocking gewünscht wurde.)
Harzliche Grüße vom Berg
http://bergpost.annerschbarrich.de
Tom
Hallo Tom,
(Ich kann es z.Zt. nur mit PHP4 ausprobieren, und das ist ja witzlos, denn da wurde
definitv totzdem gewartet, auch wenn extra NonBlocking gewünscht wurde.)
Sorry, aber das kann ich mir nicht vorstellen. Wie hast Du das denn getestet?
Folgender Testcase: Erste PHP-Datei:
<?php
$fp = dio_open ('file.txt', O_WRONLY | O_CREAT, 0666);
var_dump ($fp);
$res = dio_fcntl ($fp, F_SETLKW, array ('start' => 0, 'length' => 0, 'whence' => SEEK_SET, 'type' => F_WRLCK));
var_dump ($res);
sleep (20);
dio_close ($fp);
?>
Zweite PHP-Datei:
<?php
$fp = fopen ('file.txt', 'r+');
var_dump ($fp);
$res = flock ($fp, LOCK_EX);
var_dump ($res);
fclose ($fp);
$fp = fopen ('file.txt', 'r+');
var_dump ($fp);
$res = flock ($fp, LOCK_EX | LOCK_NB);
var_dump ($res);
fclose ($fp);
?>
Erste führst Du in einem Fenster auf, da sollte folgende Ausgabe erscheinen:
resource(4) of type (Direct I/O File Descriptor)
int(0)
(1. Zeile auf jeden Fall resource(X) und zweite Zeile auf jeden Fall int(0))
Zweite führst du während die erste noch schläft in einem zweiten Fenster aus, dann sollte sowas rauskommen:
resource(5) of type (stream)
bool(true)
resource(6) of type (stream)
bool(true)
(1. und 3. Zeile auf jeden Fall resource(X) und zweite und vierte Zeile auf jeden Fall bool(true)!)
Und das zweite Script sollte sich SOFORT beenden, d.h. auf GAR NICHTS warten.
Eben weil beide Lock-Typen (fcntl(2) und flock(2)) vollkommen unterschiedlich sind und nichts miteinander zu tun haben.
Der EINZIGE Fall, in dem auch nur IRGENDETWAS warten könnte, wäre, wenn Du Mandatory Locks aktiviert hast, d.h. das Dateisystem mit -o mand gemountet hast und das SetGid-Bid bei deaktiviertem Group-Executable-Bit der betroffenen Datei aktiviert hast - das allerdings auch nicht bei diesem Beispiel (das die Datei nur zum Lesen öffnet), sondern dann bei konkreten Lese- und Schreibvorgängen.
Viele Grüße,
Christian
Hello,
Danke für den Test.
So (ähnlich) hatte ich es gemacht.
Ich muss mich erstmal orientieren, ob ich bei PHP 5.2.0 noch mit --enable-dio neu compilieren muss oder ob ich jetzt die von Dir erwähnte Erweiterung nutzen kann.
Dann werde ich es selber nochmal ausprobieren und dann auch nochmal mit Deinem Test auf dem alten System. Vielleidht hatte ich wirklich nur was falsch gemacht. Die Dateien sind leider nur auf der alten Platte. Ich gucke aber nachher nach.
Der EINZIGE Fall, in dem auch nur IRGENDETWAS warten könnte, wäre, wenn Du Mandatory Locks aktiviert hast, d.h. das Dateisystem mit -o mand gemountet hast
so ist es
und das SetGid-Bid bei deaktiviertem Group-Executable-Bit der betroffenen Datei aktiviert hast - das allerdings auch nicht bei diesem Beispiel (das die Datei nur zum Lesen öffnet), sondern dann bei konkreten Lese- und Schreibvorgängen.
DAS muss ich allerdings erstmal verdauen. Wodurch steuere ich das? Kann das unter Normalumständen aus Versehen passieren?
Harzliche Grüße vom Berg
http://bergpost.annerschbarrich.de
Tom
Hallo Tom,
[PHP 5.1 + dio]
Das gibt's wie gesagt nur noch als nicht mehr gewartete Erweiterung. Installationsanleitungen sind übrigens:
cvs -d :pserver:cvsread@cvs.php.net:/repository co pecl/dio
cd pecl/dio
phpize
./configure
make
make install
[mandatory locking ]
DAS muss ich allerdings erstmal verdauen. Wodurch steuere ich das? Kann das unter Normalumständen aus Versehen passieren?
DASS Du -o mand verwendet hast UND die Bits Deiner Testdatei zufälligerweise so gesetzt hast? Möglich, aber EXTREM unwahrscheinlich.
Viele Grüße,
Christian
Hello,
[PHP 5.1 + dio]
Das gibt's wie gesagt nur noch als nicht mehr gewartete Erweiterung. Installationsanleitungen sind übrigens:
cvs -d :pserver:cvsread@cvs.php.net:/repository co pecl/dio
cd pecl/dio
phpize
./configure
make
make install
Danke, Du kannst wohl Gedanken lesen?
Ich suche schon die ganze Zeit, wo ich die PECL-Erweiterung downloaden kann.
Unter http://pecl.php.net/package/dio ist leider im Moment nichts zu holen.
[mandatory locking ]
DAS muss ich allerdings erstmal verdauen. Wodurch steuere ich das? Kann das unter Normalumständen aus Versehen passieren?DASS Du -o mand verwendet hast UND die Bits Deiner Testdatei zufälligerweise so gesetzt hast? Möglich, aber EXTREM unwahrscheinlich.
die Mount-Option war selbstverständlich absichtlich, sonst funktioniert es doch gar nicht (?).
Bis das wieder alles so läuft, wie auf dem alten Server, eben nur mit den neuen Versionen, das ziiiiieht sich hin. Alleine macht mir das auch immer nicht so viel Spaß. Dabei kann man um diese Jahreszeit kaum 'was anderes machen, wenn man nciht mit den Kindern der Nachbarn Laterne laufen will. :-)
Harzliche Grüße vom Berg
http://bergpost.annerschbarrich.de
Tom
Hello,
cvs -d :pserver:cvsread@cvs.php.net:/repository co pecl/dio
cd pecl/dio
phpize
./configure
make
make install
Ich krieg die losen Enden nicht zusammen! :-((
CVS ist drauf und tut's
Sourcen hatte ich vorhin beschafft und unter /opt/src/php-5.2.4 geparkt
das DIO-PECL ligt nun fröhlich unter /root/cvs/pecl/dio
Leider habe ich kein funktionstüchtiges phpize
Es gibt aber
- phpize.in
- phpize.m4
- phpize.1.in
phpize.in sieht ja ganz gut aus...
kann ich das gebrauchen?
Ich habe das PHP nicht auf der Kiste gebaut, sondern fertig per apt-get geholt.
Mit welcher Aktion kommt / entsteht phpize?
Harzliche Grüße vom Berg
http://bergpost.annerschbarrich.de
Tom
Hallo,
Sourcen hatte ich vorhin beschafft und unter /opt/src/php-5.2.4 geparkt
Wenn Du, wie Du weiter unten schreibst, PHP über apt-get installiert hast, dann nutzen Dir die PHP-Sourcen selbst nicht viel.
Ich habe das PHP nicht auf der Kiste gebaut, sondern fertig per apt-get geholt.
Dann spaltet die Distribution, die Du hast, PHP in verschiedene Pakete auf. Das Paket, in dem phpize enthalten ist, ist bei Dir offensichtlich nicht installiert. Suche mal nach einem Paket, das auf '-dev' endet, z.B. 'php-dev' oder ähnliches... Wenn das installiert ist, gibt's auch phpize. Bzw. ich sehe gerade, dass bei Debian das Paket 'php5-dev' heißt und das dann nicht 'phpize', sondern 'phpize5' heißt (die benennen das wohl auf Grund von Kompabilität mit PHP4 um).
Viele Grüße,
Christian
Hello Christian,
Dann spaltet die Distribution, die Du hast, PHP in verschiedene Pakete auf. Das Paket, in dem phpize enthalten ist, ist bei Dir offensichtlich nicht installiert. Suche mal nach einem Paket, das auf '-dev' endet, z.B. 'php-dev' oder ähnliches... Wenn das installiert ist, gibt's auch phpize. Bzw. ich sehe gerade, dass bei Debian das Paket 'php5-dev' heißt und das dann nicht 'phpize', sondern 'phpize5' heißt (die benennen das wohl auf Grund von Kompabilität mit PHP4 um).
Jo, das wird wohl doch 'ne längere Aktion.
Da werde ich mich die nächsten Tage nochmal ans Selberbauen machen auf der nächsten Platte.
Also alles nochmal von vorne. Irgendwann kann ich's dann hoffentlich auswendig.
Nun suche ich erstmal nach einem dev-Paket für die Fertiglösung
Soweit war ich vorhin schon einmal, hab dann aber abbrechen müssen :-(
Bis später bestimmt.
Mal schauen, wer dann noch hier ist.
Harzliche Grüße vom Berg
http://bergpost.annerschbarrich.de
Tom
Hello,
Bis später bestimmt.
Mal schauen, wer dann noch hier ist.
ging jetzt schneller, als ich dachte.
Build complete.
(It is safe to ignore warnings about tempnam and tmpnam).
testserver:~/cvs/pecl/dio# make install
Installing shared extensions: /usr/lib/php5/20060613+lfs/
testserver:~/cvs/pecl/dio#
Zwischendurch hat das "apt-get install php5-dev" noch das Postgre DBMS runtergeknallt. Mal sehen, ob es noch funktioniert...
Aber sonst sieht es jetzt gut aus.
Der Vollständigkeit halber:
in der php.ini das extension_dir setzen
;;;;;;;;;;;;;;;;;;;;;;;;;
; Paths and Directories ;
;;;;;;;;;;;;;;;;;;;;;;;;;
; UNIX: "/path1:/path2"
;include_path = ".:/usr/share/php"
;
; Windows: "\path1;\path2"
;include_path = ".;c:\php\includes"
; The root of the PHP pages, used only if nonempty.
; if PHP was not compiled with FORCE_REDIRECT, you SHOULD set doc_root
; if you are running php as a CGI under any web server (other than IIS)
; see documentation for security issues. The alternate is to use the
; cgi.force_redirect configuration below
doc_root =
; The directory under which PHP opens the script using /~username used only
; if nonempty.
user_dir =
; Directory in which the loadable extensions (modules) reside.
; extension_dir = "./"
extension_dir = /usr/lib/php5/20060613+lfs/
und die extension zum automatsichen laden anmelden
;;;;;;;;;;;;;;;;;;;;;;
; Dynamic Extensions ;
;;;;;;;;;;;;;;;;;;;;;;
;
; If you wish to have an extension loaded automatically, use the following
; syntax:
;
; extension=modulename.extension
;
; For example, on Windows:
;
; extension=msql.dll
;
; ... or under UNIX:
;
; extension=msql.so
extension=dio.so
und da sind sie!
[316] => deg2rad
[852] => dgettext
+--> [1092] => dio_close
| [1089] => dio_fcntl
| [1085] => dio_open
| [1090] => dio_read
| [1088] => dio_seek
| [1087] => dio_stat
| [1093] => dio_tcsetattr
| [1086] => dio_truncate
+--> [1091] => dio_write
[510] => dir
[193] => dirname
Der Index davor stammt aus meiner Abfrage mit get_defined_functions
http://de2.php.net/manual/de/function.get-defined-functions.php
Nun noch ausprobieren, ob sie auch das tun, was sie sollen...
dann geb ich dreie aus. *freu*
Harzliche Grüße vom Berg
http://bergpost.annerschbarrich.de
Tom
Hello,
(Ich kann es z.Zt. nur mit PHP4 ausprobieren, und das ist ja witzlos, denn da wurde
definitv totzdem gewartet, auch wenn extra NonBlocking gewünscht wurde.)Sorry, aber das kann ich mir nicht vorstellen. Wie hast Du das denn getestet?
Ich hatte das im Prinzip so:
$fp = dio_open ('file.txt', O_RDWR | O_CREAT, 2666);
----
$res = dio_fcntl ($fp, F_SETLK, array ('start' => 0, 'length' => 400, 'whence' => SEEK_SET, 'type' => F_WRLCK));
Einen Fehler habe ich entdeckt, der Mode stand bei mir auf 2666, wie auch immer ich damals darauf gekommen bin. Ich weiß auch nicht mal mehr, was das sollte.
Das hat aber nicht das fehlerhafte Verhalten ausgelöst.
Ih habe es eben mit den alten Dateien und mit Deinen nochmals auf PHP 5.2.0 und auf dem alten System mit 4.3.8 ausprobiert. Vom Verhalten kein Unterschied.
Meine Aufzeichnungen stammen aber noch von einem 4.2.x-System (?)
Das kannte noch kein file_get_contents()
Da hat es nicht funktioniert.
Ich habe damals den vermeintlichen Bug an PHP gesendet und mir heiße Ohren eingehandelt, das müsse so sein. Kann sein, dass sie es daraufhin repariert haben.
Ob ich allerdings DIE Platte auch noch irgendwo habe, weiß ich nicht.
Für heute ist jedenfalls erstmal Schluss.
Und, *oh Freude* es funktioniert zur Abwechslung mal alles. :-))
Vielen Dank nochmal für Deine Unterstütung.
Bald dürfte dem System nichts mehr fehlen.
Harzliche Grüße vom Berg
http://bergpost.annerschbarrich.de
Tom
echo $begrüßung;
Und _wenn_ die() hier jemals ausgeführt würde, wäre es nicht unbedingt geschickt, ein größeres Skript derartig abzubrechen. Es sollte dann eine qualifizierte Fehlerbehandlung stattfinden, die den User nicht in den Wald stellt.
Ich wollte das Beispiel möglichst einfach halten, so dass wirklich nur der eigentliche Sachverhalt dargestellt wird - und bei diesen einfachen Beispielen, die nichts anderes tun, funktioniert die Fehlerbehandlung über die() zufriedenstellend.
Für das Beispiel würde es meiner Meinung nach auch reichen, dass die Fehlerbehandlung nur angedeutet wird, z.B. mit einem Kommentar. Das Auswerten der Rückgabewerte, also das Reagieren auf Fehlerzustände ist vollkommen in Ordnung. Nur ist es leider in Beispielen üblich, die Behandlung mal eben fix mit die() auszuführen. Und ebenfalls üblich ist es, dass unbedarfte Anwender solche Beispiele nicht genügend reflektiert habend übernehmen. Das ist der Grund, warum ich solche die()-Beispiele nicht gutheiße (und Tom ja offensichtlich auch nicht).
Wenn ich dort jetzt noch eine ausgereifte Fehlerbehandlung einbauen würde, dann würde ich das Beispiel nur unnötig verkomplizieren.
Das halte ich auch nicht für erforderlich. Ein Beispiel soll zeigen, wie es geht, muss aber nicht unbedingt komplett copy'n'paste-ausführbar sein. (Meinetwegen könnten auch <?php, ?> und die Shebang-Zeilen wegbleiben.)
Leider sind solche ja durchaus "funzenden" Beispiele geeignet, sich über den Weg des Copy & Paste beliebig oft zu vervielfältigen und sich dann als Standart (sic!) zu etablieren.
Ich habe kein Problem damit, wenn sich der aktuelle Code als Standard etablieren würde, denn ich kann Deine Kritik nicht nachvollziehen (s.o.).
Dass du mit dem Locking-Artikel die allgemeine Scripting-Qualität erhöhen möchtest, ist lobenswert und nicht zu kritisieren. Der Artikel wird sicher in Zukunft hier oft verlinkt werden (müssen). Nur gibst du hier nicht nur ein gutes Beispiel, wie man es besser machen kann, sondern mit dem Sterbenlassen im Fehlerfall auch ein gutes Beispiel wie man es nicht machen sollte. Gerade, wenn man als Vorbild gilt, sollte man auch solche Kollateralschadensquellen beachten (gelingt nicht immer, aber versuchen sollte man es stets).
Du musst dich jetzt nicht unbedingt genötigt fühlen, die Beispiele umzuarbeiten. Es würde auch reichen, wenn du da Fußnoten hinzufügtest, die auf den (noch zu schre<I>benden) Artikel zu Fehleraufspürmöglichkeiten und zur fehlertoleranten Programmierung im Allgemeinen verwiesen. :-)
echo "$verabschiedung $name";
Hallo dedlfix,
Ich werde mir Deine Anmerkungen mal in Ruhe durch den Kopf gehen lassen und mir überlegen ob ich was ändere bzw. was ich am Artikel ändern werde (was dann u.U. auch die anderen Sprachen betrifft).
Viele Grüße,
Christian
Hello,
Anmerkung zum PHP-Teil:
if (!flock ($fp, LOCK_EX)) {
die ('Sperren der Datei fehlgeschlagen!')};
Ich habe das eben nochmals ausprobiert. (Aber Vorsicht, mein Debian ist anders als andere ... :-P)
Die() wird im Leben nicht ausgeführt, wenn die Datei schon mit LOCK_SH oder LOCK_EX oder auch mit einem dio_lock geöffnet ist.
Die übliche max_execution_time von 30 Sekunden ermittelt sich sicherlich wieder aus der verbrauchten Prozessorzeit des Skriptes. Da aber das flock() intern immer wieder auf ein "sleep" läuft, bevor es den nächsten Lockversuch vornimmt und sleep keine (kaum) Prozessorzeit verbraucht, dauert das Timeout ewig. Ich habe es in einer halben STUNDE nicht erreicht, obwohl 30 Sekunden eingestellt sind.
Harzliche Grüße vom Berg
http://bergpost.annerschbarrich.de
Tom
Hallo Tom,
Die() wird im Leben nicht ausgeführt, wenn die Datei schon mit LOCK_SH oder LOCK_EX oder auch mit einem dio_lock geöffnet ist.
Wenn ein anderes Programm das Lock hat: Nein, natürlich nicht, dafür brauche ich auch das die() nicht. (siehe mein anderes Posting)
Die übliche max_execution_time von 30 Sekunden ermittelt sich sicherlich wieder aus der verbrauchten Prozessorzeit des Skriptes.
Ja.
Da aber das flock() intern immer wieder auf ein "sleep" läuft, bevor es den nächsten Lockversuch vornimmt
Nein. Bei ALLEN Systemaufrufen legt sich der Prozess *GRUNDSÄTZLICH* schlafen - sleep() ist halt ein Systemaufruf, bei dem der Kernel auf X Sekunden wartet, flock() einer, bei dem der Kernel auf das Freiwerden der Sperre wartet, während read() ein Systemaufruf ist, bei dem der Kernel Daten holt (und evtl. auf diese wartet).
Ich habe es in einer halben STUNDE nicht erreicht, obwohl 30 Sekunden eingestellt sind.
Du wirst das Timeout NIE erreichen, weil solange flock() nicht zurückgekehrt ist, die Kontrolle NIE an den PHP-Prozess zurückgegeben wird - und damit der Zeitzähler NIE hochlaufen wird.
Viele Grüße,
Christian
Hello,
Du wirst das Timeout NIE erreichen, weil solange flock() nicht zurückgekehrt ist, die Kontrolle NIE an den PHP-Prozess zurückgegeben wird - und damit der Zeitzähler NIE hochlaufen wird.
Danke.
Harzliche Grüße vom Berg
http://bergpost.annerschbarrich.de
Tom