Ich habe 10 Prozesse, die versuchen, dieselbe Datei mehr oder weniger gleichzeitig mit dem Aufruf open(O_CREAT) zu öffnen und sie dann zu löschen. Gibt es einen robusten Weg, um herauszufinden, welcher Prozess die Datei tatsächlich erstellt hat und welcher Prozess die bereits erstellte Datei geöffnet hat, wenn ich beispielsweise genau zählen möchte, wie oft diese Datei in einem solchen Szenario geöffnet wurde?
Ich denke, ich könnte einen globalen Mutex auf den Dateiöffnungsvorgang setzen und eine Folge von open () -Aufrufen mit O_CREAT- und O_EXCL-Flags ausführen, aber das passt nicht zu meiner Definition von “robust”.
Verwenden O_EXCL
Flagge mit O_CREAT
. Dies schlägt fehl, wenn die Datei existiert und errno auf gesetzt wird EEXIST
. Wenn dies fehlschlägt, versuchen Sie es erneut ohne O_CREAT
und ohne O_EXCL
Modi.
z.B
int fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0644);
if ((fd == -1) && (EEXIST == errno))
{
/* open the existing file with write flag */
fd = open(path, O_WRONLY);
}
Basierend auf Ihren Kommentaren möchten Sie etwas in der Art dieser Funktion:
/* return the fd or negative on error (check errno);
how is 1 if created, or 0 if opened */
int create_or_open (const char *path, int create_flags, int open_flags,
int *how) {
int fd;
create_flags |= (O_CREAT|O_EXCL);
open_flags &= ~(O_CREAT|O_EXCL);
for (;;) {
*how = 1;
fd = open(path, create_flags);
if (fd >= 0) break;
if (errno != EEXIST) break;
*how = 0;
fd = open(path, open_flags);
if (fd >= 0) break;
if (errno != ENOENT) break;
}
return fd;
}
Diese Lösung ist nicht kugelsicher. Es kann Fälle geben (symbolische Links vielleicht?), die zu einer Endlosschleife führen würden. Außerdem kann es in bestimmten Parallelitätsszenarien zu einer Live-Sperre kommen. Ich lasse die Lösung solcher Probleme als Übung. 🙂
In Ihrer bearbeiteten Frage stellen Sie:
Ich habe 10 Prozesse, die versuchen, dieselbe Datei mehr oder weniger gleichzeitig mit dem Aufruf open(O_CREAT) zu öffnen und sie dann zu löschen.
Eine hackige, aber kugelsicherere Lösung wäre, jedem Prozess eine andere Benutzer-ID zu geben. Dann verwenden Sie einfach die regulären open(path, O_CREAT|...)
Anruf. Anschließend können Sie die Datei mit abfragen fstat()
auf dem Dateideskriptor, und überprüfen Sie die st_uid
Feld der stat
Struktur. Wenn das Feld der Benutzer-ID des Prozesses entspricht, dann war es der Ersteller. Ansonsten war es ein Opener. Das funktioniert, da jeder Prozess die Datei nach dem Öffnen löscht.
Verwenden
(O_CREAT|O_EXCL)
um einen Fehler zu erhalten, wenn die Datei bereits existiert. Wenn Sie den Fehler erhalten, überprüfen Sie dieerrno
um zu sehen, ob es existiert, dann öffnen Sie es erneut, wie Sie es öffnen möchten, da Sie wissen, dass es bereits existiert.– jxh
3. April 2013 um 21:29 Uhr
Und dann was tun? Aber was ist, wenn ein anderer Prozess es nach meiner Überprüfung, aber vor meinem “erneut öffnen, wie ich möchte” öffnet?
– Sergej
3. April 2013 um 21:30 Uhr
Dein Problem ist in deiner Beschreibung dann nicht vollständig spezifiziert. Aktualisieren Sie Ihre Frage mit dem tatsächlichen Problem, mit dem Sie konfrontiert sind. Zeigen Sie etwas Code und weisen Sie darauf hin, wo etwas nicht so abläuft, wie Sie es erwarten.
– jxh
3. April 2013 um 21:37 Uhr
Danke, den wichtigen Teil habe ich fett markiert.
– Sergej
3. April 2013 um 21:38 Uhr
Die klassische Redewendung, bevor es ein O_CREAT gab, war zu callen
open()
um eine vorhandene Datei zu öffnen undcreat()
um es zu schaffen, wenn dieopen()
gescheitert. Dascreat()
Die Funktion soll so implementiert werden, als ob sie es wäreint creat(const char *path, mode_t mode) { return open(path, O_WRONLY|O_CREAT|O_TRUNC, mode); }
Daher empfehle ich es nicht besonders, aber es gibt keine andere Möglichkeit zu wissen, ob Sie eine neue Datei erstellt oder eine vorhandene geöffnet haben (während Sie beides tun). Es gibt ein TOCTOU-Problem mit der Open/Creat- oder Open/Open-Technik.– Jonathan Leffler
3. April 2013 um 21:38 Uhr