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.

Benutzer-Avatar
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.

Im Buch Die Linux-Programmierschnittstelle Einer der möglichen Parameter für shmctl() wird wie folgt beschrieben.

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.

Benutzer-Avatar
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:

  1. 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.

  2. 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.

    Diese Lösung wurde zusätzlich zu boost.interprocess implementiert.

  • 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

Benutzer-Avatar
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

Benutzer-Avatar
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.

1054650cookie-checkGemeinsam genutzten Posix-Speicher entfernen, wenn er nicht verwendet wird?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy