Gemeinsam genutzten Posix-Speicher entfernen, wenn er nicht verwendet wird?
Lesezeit: 10 Minuten
Gibt es eine Möglichkeit, linuxspezifisch oder nicht, Posix-Shared-Memory-Segmente zu haben (erhalten mit shm_open()) entfernt, wenn kein Prozess sie verwendet. d. h. sie werden als Referenz gezählt und vom System entfernt, wenn die Referenz 0 wird
Ein paar Anmerkungen:
Das Einrichten eines atexit-Handlers, um sie zu entfernen, funktioniert nicht, wenn das Programm abstürzt.
Derzeit bette ich auf Linux-spezifische Weise die PID in den Segmentnamen ein und versuche, unbenutzte Segmente zu finden, indem ich /dev/shm in einem externen Programm durchlaufe. Was den Nachteil hat, dass sie regelmäßig extern auf ziemlich hackige Weise aufgeräumt werden müssen.
Da das Programm mehrere Kopien ausführen kann, ist die Verwendung eines wohldefinierten Namens für das Segment, das das Programm beim Start wiederverwendet, nicht möglich.
Fragen Sie, ob es dafür einen Systembibliotheksansatz gibt, anstatt es von Hand zu tun?
– Jo
14. November 2012 um 12:03 Uhr
Sie könnten gdb verwenden, um Ihre Anwendung zu debuggen, damit sie nicht abstürzt? Das mildert das Problem, dass abstürzende Anwendungen nicht hinter sich selbst aufräumen können …
– Nicolas Wilson
14. November 2012 um 12:37 Uhr
Wenn es einen Punkt in der Ausführung Ihres Programms gibt, an dem bekannt ist, dass alle Prozesse, die das gemeinsame Speichersegment öffnen müssen, dies bereits getan haben, können Sie die Verknüpfung sicher aufheben. Durch das Aufheben der Verknüpfung wird das Objekt aus dem globalen Namensraum entfernt, aber es bleibt bestehen, solange es mindestens einen Prozess gibt, der seinen Dateideskriptor offen hält. Wenn nach diesem Punkt ein Absturz auftritt, wird der Dateideskriptor automatisch geschlossen und der Referenzzähler wird dekrementiert. Sobald keine offenen Deskriptoren für den nicht verknüpften gemeinsam genutzten Speicherblock mehr übrig sind, wird er gelöscht.
Dies ist im folgenden Szenario nützlich: Ein Prozess erstellt einen gemeinsam genutzten Speicherblock, hebt die Verknüpfung auf und verzweigt sich dann. Das Kind erbt den Dateideskriptor und kann den gemeinsam genutzten Speicherblock verwenden, um mit dem Elternteil zu kommunizieren. Sobald beide Prozesse beendet sind, wird die Sperre automatisch entfernt, wenn beide Dateideskriptoren geschlossen werden.
Während er nicht verknüpft ist, ist der gemeinsam genutzte Speicherblock für andere Prozesse nicht verfügbar, um ihn zu öffnen. Mittlerweile, wenn man verwendet shm_open() mit demselben Namen wie der nicht verknüpfte Block, würde stattdessen ein neuer und völlig anderer Shared-Memory-Block erstellt werden.
Nein – zumindest unter Linux enthält der Kernel nichts, was dies tun kann. Es liegt an einer Anwendung, irgendwann shm_unlink() aufzurufen, um ein gemeinsames Speichersegment loszuwerden.
Bobax
Ich habe einen Weg gefunden, einen Systembefehl und den Linux-Befehl “fuser” zu verwenden, mit denen die Prozesse aufgelistet werden können, die eine Datei geöffnet haben. Auf diese Weise können Sie überprüfen, ob die Shared-Memory-Datei (befindet sich in /dev/shm”) noch verwendet wird, und sie gegebenenfalls löschen. Beachten Sie, dass die Operationen check / delete / create in einem kritischen Abschnitt zwischen Prozessen eingeschlossen sein müssen Verwenden eines benannten Mutex oder benannten Semaphors oder einer Dateisperre.
std::string shm_file = "/dev/shm/" + service_name + "Shm";
std::string cmd_line = "if [ -f " + shm_file + " ] ; then if ! fuser -s " + shm_file + " ; then rm -f " + shm_file + " ; else exit 2 ; fi else exit 3 ; fi";
int res = system(cmd_line.c_str());
switch (WEXITSTATUS(res)) {
case 0: _logger.warning ("The shared memory file " + shm_file + " was found orphan and is deleted"); break;
case 1: _logger.critical("The shared memory file " + shm_file + " was found orphan and cannot be deleted"); break;
case 2: _logger.trace ("The shared memory file " + shm_file + " is linked to alive processes"); break;
case 3: _logger.trace ("The shared memory file " + shm_file + " is not found"); break;
}
Für den gemeinsam genutzten Speicher, der mit der sysV-API erstellt wurde, ist ein solches Verhalten möglich. Nur unter Linux. Es ist kein gemeinsam genutzter POSIX-Speicher, kann aber für Sie funktionieren.
IPC_RMID Markieren Sie das Shared-Memory-Segment und die zugehörige shmid_ds-Datenstruktur zum Löschen. Wenn derzeit kein Prozess das Segment angehängt hat, erfolgt die Löschung sofort; andernfalls wird das Segment entfernt, nachdem sich alle Prozesse davon gelöst haben (dh wenn der Wert des Felds shm_nattch in der Datenstruktur shmid_ds auf 0 fällt). In einigen Anwendungen können wir dafür sorgen, dass ein gemeinsam genutztes Speichersegment beim Beenden der Anwendung sauber gelöscht wird, indem wir es sofort zum Löschen markieren, nachdem alle Prozesse es mit shmat() an ihren virtuellen Adressraum angehängt haben. Dies ist analog zum Aufheben der Verknüpfung einer Datei, nachdem wir sie geöffnet haben. Wenn unter Linux ein gemeinsam genutztes Segment mit IPC_RMID zum Löschen markiert, aber noch nicht entfernt wurde, weil es noch an einen Prozess angehängt ist, kann ein anderer Prozess dieses Segment anhängen. Dieses Verhalten ist jedoch nicht übertragbar: Die meisten UNIX-Implementierungen verhindern neue Anhänge an ein zum Löschen markiertes Segment. (SUSv3 schweigt sich darüber aus, welches Verhalten in diesem Szenario auftreten sollte.) Einige Linux-Anwendungen hängen mittlerweile von diesem Verhalten ab, weshalb Linux nicht geändert wurde, damit es zu anderen UNIX-Implementierungen passt.
Sebastian
Nehmen wir den kompliziertesten Fall an:
Sie haben mehrere Prozesse, die über Shared Memory kommunizieren
Sie können jederzeit beginnen und enden, auch mehrmals. Das bedeutet, dass es weder einen Master-Prozess noch einen dedizierten “ersten” Prozess gibt, der den gemeinsam genutzten Speicher initialisieren kann.
Das bedeutet zB, dass es keinen Punkt gibt, an dem Sie den gemeinsamen Speicher sicher trennen können, sodass weder die Antworten von Sergey noch von Hristo funktionieren.
Ich sehe zwei mögliche Lösungen und würde mich über Feedback freuen, da das Internet zu dieser Frage schrecklich still ist:
Speichern Sie die PID (oder eine spezifischere Prozesskennung, falls Sie eine haben) des letzten Prozesses, der in den gemeinsamen Speicher geschrieben hat, innerhalb des gemeinsamen Speichers als Sperre. Dann könnten Sie etw. tun. wie der folgende Pseudocode:
int* pshmem = open shared memory()
while(true)
nPid = atomic_read(pshmem)
if nPid = 0
// your shared memory is in a valid state
break
else
// process nPid holds a lock to your shared memory
// or it might have crashed while holding the lock
if process nPid still exists
// shared memory is valid
break
else
// shared memory is corrupt
// try acquire lock
if atomic_compare_exchange(pshmem, nPid, my pid)
// we have the lock
reinitialize shared memory
atomic_write(pshem, 0) // release lock
else
// somebody else got the lock in the meantime
// continue loop
Dies bestätigt, dass der letzte Schreiber nicht beim Schreiben gestorben ist. Der gemeinsame Speicher bleibt immer noch länger bestehen als jeder Ihrer Prozesse.
Verwenden Sie eine Lese-/Schreib-Dateisperre, um herauszufinden, ob irgendein Prozess der erste Prozess ist, der das Shared-Memory-Objekt öffnet. Der erste Prozess kann dann den gemeinsamen Speicher neu initialisieren:
// try to get exclusive lock on lockfile
int fd = open(lockfile, O_RDONLY | O_CREAT | O_EXLOCK | O_NONBLOCK, ...)
if fd == -1
// didn't work, somebody else has it and is initializing shared memory
// acquire shared lock and wait for it
fd = open(lockfile, O_RDONLY | O_SHLOCK)
// open shared memory
else
// we are the first
// delete shared memory object
// possibly delete named mutex/semaphore as well
// create shared memory object (& semaphore)
// degrade exclusive lock to a shared lock
flock(fd, LOCK_SH)
Dateisperren scheinen der einzige (?) Mechanismus auf POSIX-Systemen zu sein, der automatisch aufgehoben wird, wenn der Prozess stirbt. Leider, Die Liste der Vorbehalte, sie zu verwenden, ist sehr, sehr lang. Der Algorithmus geht davon aus flock wird auf dem zugrunde liegenden Dateisystem zumindest auf dem lokalen Rechner unterstützt. Dem Algorithmus ist es egal, ob die Sperren tatsächlich für andere Prozesse auf NFS-Dateisystemen sichtbar sind oder nicht. Sie müssen lediglich für alle Prozesse sichtbar sein, die auf das Shared-Memory-Objekt zugreifen.
Die gemeinsame Dateisperre ist ziemlich interessant, danke Sebastian! Eine Frage. Das Beispiel geht davon aus, dass die Datei nicht existiert, was möglicherweise nicht der Fall ist. Was passiert dann?
– SRG
19. Mai 2019 um 18:41 Uhr
open(lockfile, O_RDONLY | O_CREAT | O_EXLOCK | O_NONBLOCK, ...) erstellt die Datei, wenn sie nicht existiert, öffnet sie jedoch, wenn sie bereits existiert.
– Sebastian
7. Juni 2019 um 12:33 Uhr
Ja, aber mein Punkt war, dass, wenn die Datei bereits existiert, wer der Eigentümer der Sperre ist? Würde es dann nicht bedeuten, dass jeder Prozess die “else-Anweisung” akzeptiert?
– SRG
10. Juni 2019 um 1:03 Uhr
Ich bin mir immer noch nicht sicher, ob ich es verstehe. Ob die Datei existiert oder nicht spielt hier keine Rolle. Wichtig ist nur, ob jemand sie geöffnet und ein Schloss erworben hat. Ich versuche, die Datei zu öffnen und gleichzeitig eine exklusive Sperre zu erlangen (öffnen mit EX_LOCK). Das gelingt nur, wenn die Datei noch nicht geöffnet ist. Es wird nur für den ersten Prozess erfolgreich sein, der dann für die Initialisierung der Sperre verantwortlich ist. Dann schaltet es die Sperre auf eine gemeinsame Sperre um und alle anderen Prozesse können im Wenn-Fall fortfahren.
– Sebastian
11. Juni 2019 um 6:56 Uhr
Vielen Dank, @Sebastian! Ich habe in meiner vorherigen Frage ein wenig durcheinander gebracht (ich meinte den Wenn-Fall), aber Ihre Antwort hat mich trotzdem dazu gebracht, es besser zu verstehen. Nochmals vielen Dank für die Klarstellungen!
– SRG
12. Juni 2019 um 8:57 Uhr
Jo
Könnten Sie nicht einfach ein globales Zählsemaphor verwenden, um die Zählung zu referenzieren? Wickeln Sie die Attach- und Detach-Aufrufe so ein, dass das Semaphor inkrementiert wird, wenn Sie es mit dem Speicher verbinden, und dekrementiert wird, wenn Sie es trennen. Geben Sie das Segment frei, wenn ein Trennen das Semaphor auf Null reduziert.
Die gemeinsame Dateisperre ist ziemlich interessant, danke Sebastian! Eine Frage. Das Beispiel geht davon aus, dass die Datei nicht existiert, was möglicherweise nicht der Fall ist. Was passiert dann?
– SRG
19. Mai 2019 um 18:41 Uhr
open(lockfile, O_RDONLY | O_CREAT | O_EXLOCK | O_NONBLOCK, ...) erstellt die Datei, wenn sie nicht existiert, öffnet sie jedoch, wenn sie bereits existiert.
– Sebastian
7. Juni 2019 um 12:33 Uhr
Ja, aber mein Punkt war, dass, wenn die Datei bereits existiert, wer der Eigentümer der Sperre ist? Würde es dann nicht bedeuten, dass jeder Prozess die “else-Anweisung” akzeptiert?
– SRG
10. Juni 2019 um 1:03 Uhr
Ich bin mir immer noch nicht sicher, ob ich es verstehe. Ob die Datei existiert oder nicht spielt hier keine Rolle. Wichtig ist nur, ob jemand sie geöffnet und ein Schloss erworben hat. Ich versuche, die Datei zu öffnen und gleichzeitig eine exklusive Sperre zu erlangen (öffnen mit EX_LOCK). Das gelingt nur, wenn die Datei noch nicht geöffnet ist. Es wird nur für den ersten Prozess erfolgreich sein, der dann für die Initialisierung der Sperre verantwortlich ist. Dann schaltet es die Sperre auf eine gemeinsame Sperre um und alle anderen Prozesse können im Wenn-Fall fortfahren.
– Sebastian
11. Juni 2019 um 6:56 Uhr
Vielen Dank, @Sebastian! Ich habe in meiner vorherigen Frage ein wenig durcheinander gebracht (ich meinte den Wenn-Fall), aber Ihre Antwort hat mich trotzdem dazu gebracht, es besser zu verstehen. Nochmals vielen Dank für die Klarstellungen!
– SRG
12. Juni 2019 um 8:57 Uhr
Wer bin ich
Nicht sicher, ob das Folgende funktioniert oder machbar ist. Aber mein Versuch.
Warum führen Sie nicht das Hilfsprogramm aus, das jedes Mal ausgeführt wird, wenn Ihr Programm abstürzt.
dh:
/proc/sys/kernel/core_pattern to /path/to/Myprogram %p
Mein Programm wird ausgeführt, wenn der Prozess abstürzt, und wahrscheinlich können Sie weiter nachforschen.
sehen
man 5 core. for more information.
Hoffe das hilft bis zu einem gewissen Grad.
10546500cookie-checkGemeinsam genutzten Posix-Speicher entfernen, wenn er nicht verwendet wird?yes
Fragen Sie, ob es dafür einen Systembibliotheksansatz gibt, anstatt es von Hand zu tun?
– Jo
14. November 2012 um 12:03 Uhr
Sie könnten gdb verwenden, um Ihre Anwendung zu debuggen, damit sie nicht abstürzt? Das mildert das Problem, dass abstürzende Anwendungen nicht hinter sich selbst aufräumen können …
– Nicolas Wilson
14. November 2012 um 12:37 Uhr