mmap-, msync- und Linux-Prozessbeendigung

Lesezeit: 9 Minuten

Benutzer-Avatar
BD in Rivenhill

Ich möchte mmap verwenden, um die Persistenz bestimmter Teile des Programmstatus in einem C-Programm zu implementieren, das unter Linux ausgeführt wird, indem ich eine Struktur mit fester Größe mit einem bekannten Dateinamen unter Verwendung von mmap() mit gesetztem MAP_SHARED-Flag verknüpfe. Aus Leistungsgründen würde ich es vorziehen, msync() überhaupt nicht aufzurufen, und keine anderen Programme werden auf diese Datei zugreifen. Wenn mein Programm beendet und neu gestartet wird, ordnet es dieselbe Datei erneut zu und verarbeitet sie, um den Zustand wiederherzustellen, in dem es sich vor der Beendigung befand. Meine Frage lautet: Wenn ich niemals msync() für den Dateideskriptor aufrufe, garantiert der Kernel, dass alle Aktualisierungen des Speichers auf die Festplatte geschrieben werden und anschließend wiederherstellbar sind, selbst wenn mein Prozess mit SIGKILL beendet wird? Wird es außerdem einen allgemeinen System-Overhead geben, da der Kernel regelmäßig die Seiten auf die Festplatte schreibt, selbst wenn mein Programm nie msync() aufruft?

BEARBEITEN: Ich habe das Problem gelöst, ob die Daten geschrieben werden, aber ich bin mir immer noch nicht sicher, ob dies zu einem unerwarteten Systemladen führen wird, wenn ich versuche, dieses Problem mit open()/write()/fsync() zu lösen und die Risiko, dass einige Daten verloren gehen könnten, wenn der Prozess von KILL/SEGV/ABRT/etc. getroffen wird. Ein ‘linux-kernel’-Tag hinzugefügt, in der Hoffnung, dass sich eine sachkundige Person melden könnte.

Benutzer-Avatar
Patrick Schlüter

Ich habe einen Kommentar von Linus Torvalds gefunden, der diese Frage beantwortet
http://www.realworldtech.com/forum/?threadid=113923&curpostid=114068

Die abgebildeten Seiten sind Teil des Dateisystem-Cache, was bedeutet, dass selbst wenn der Benutzerprozess, der eine Änderung an dieser Seite vorgenommen hat, stirbt, die Seite immer noch vom Kernel verwaltet wird und alle gleichzeitigen Zugriffe auf diese Datei durch den Kernel gehen, andere Prozesse werden aus diesem Cache bedient. In manchen alten Linux-Kerneln war das anders, deshalb wird in manchen Kernel-Dokumenten immer noch von force gesprochen msync.

EDIT: Danke RobH hat den Link korrigiert.

BEARBEITEN:

Seit Linux 4.15 wird ein neues Flag, MAP_SYNC, eingeführt, das die Kohärenz gewährleisten kann.

Shared-File-Mappings mit diesem Flag stellen sicher, dass ein Teil des Speichers zwar schreibbar im Adressraum des Prozesses abgebildet wird, aber in derselben Datei am selben Offset sichtbar ist, selbst nachdem das System abstürzt oder neu gestartet wird.

Verweise:

http://man7.org/linux/man-pages/man2/mmap.2.html Suche MAP_SYNC auf der Seite

https://lwn.net/Articles/731706/

  • Beachten Sie, dass gemäß POSIX msync hat nichts mit dieser alten Linux-Anforderung zu tun, um sicherzustellen, dass Daten vor dem Aufheben/Beenden gespeichert werden. Es ist rein analog zu fsync und für Dinge wie die Verwaltung der Datenintegrität im Falle eines Hardware-/Stromausfalls gedacht (und, wenn ich mich erinnere, Aktualisierungen der Dateiänderungszeit).

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    27. September 2011 um 23:55 Uhr

  • Dieser Link kann alt sein. Dies ist eine direkte Verknüpfung zum (meiner Meinung nach) relevantesten Kommentar:

    – RobH

    14. März 2013 um 8:14 Uhr


Ich beschloss, weniger faul zu sein und die Frage, ob die Daten auf die Festplatte geschrieben werden, endgültig zu beantworten, indem ich etwas Code schrieb. Die Antwort ist, dass es geschrieben wird.

Hier ist ein Programm, das sich abrupt beendet, nachdem es einige Daten in eine mmap-Datei geschrieben hat:

#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>

typedef struct {
  char data[100];
  uint16_t count;
} state_data;

const char *test_data = "test";

int main(int argc, const char *argv[]) {
  int fd = open("test.mm", O_RDWR|O_CREAT|O_TRUNC, (mode_t)0700);
  if (fd < 0) {
    perror("Unable to open file 'test.mm'");
    exit(1);
  }
  size_t data_length = sizeof(state_data);
  if (ftruncate(fd, data_length) < 0) {
    perror("Unable to truncate file 'test.mm'");
    exit(1);
  }
  state_data *data = (state_data *)mmap(NULL, data_length, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_POPULATE, fd, 0);
  if (MAP_FAILED == data) {
    perror("Unable to mmap file 'test.mm'");
    close(fd);
    exit(1);
  }
  memset(data, 0, data_length);
  for (data->count = 0; data->count < 5; ++data->count) {
    data->data[data->count] = test_data[data->count];
  }
  kill(getpid(), 9);
}

Hier ist ein Programm, das die resultierende Datei validiert, nachdem das vorherige Programm tot ist:

#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>

typedef struct {
  char data[100];
  uint16_t count;
} state_data;

const char *test_data = "test";

int main(int argc, const char *argv[]) {
  int fd = open("test.mm", O_RDONLY);
  if (fd < 0) {
    perror("Unable to open file 'test.mm'");
    exit(1);
  }
  size_t data_length = sizeof(state_data);
  state_data *data = (state_data *)mmap(NULL, data_length, PROT_READ, MAP_SHARED|MAP_POPULATE, fd, 0);
  if (MAP_FAILED == data) {
    perror("Unable to mmap file 'test.mm'");
    close(fd);
    exit(1);
  }
  assert(5 == data->count);
  unsigned index;
  for (index = 0; index < 4; ++index) {
    assert(test_data[index] == data->data[index]);
  }
  printf("Validated\n");
}

  • Nun, streng genommen beweist ein Testprogramm allein nicht, dass das zuverlässig funktioniert. Daher sollte man sich stattdessen auf eine maßgebliche Spezifikation/Dokumentation/Erklärung verlassen.

    – maxschlepzig

    28. Oktober 2018 um 11:31 Uhr

Benutzer-Avatar
zach

Ich habe etwas gefunden, das zu meiner Verwirrung beiträgt:

munmap wirkt sich nicht auf das zugeordnete Objekt aus, das heißt, Der Aufruf von munmap bewirkt nicht, dass der Inhalt der abgebildeten Region in die Plattendatei geschrieben wird. Die Aktualisierung der Festplattendatei für eine MAP_SHARED-Region erfolgt automatisch durch den virtuellen Speicheralgorithmus des Kernels, wenn wir in der speicherabgebildeten Region speichern.

hieraus ist ein Auszug Fortgeschrittene Programmierung in der UNIX®-Umgebung.

aus der Linux-Manpage:

MAP_SHARED Teilen Sie diese Zuordnung mit allen anderen Prozessen, die dieses Objekt abbilden. Das Speichern in der Region entspricht dem Schreiben in die Datei. Die Datei wird möglicherweise erst aktualisiert, wenn msync(2) oder munmap(2) aufgerufen werden.

die beiden scheinen widersprüchlich. ist APUE falsch?

  • Ich las den Kernel-Code über munmap versucht herauszufinden, wann und wie ist munmap Schreiben Sie den Cache zurück in die Datei. Aber aus dem Code wurde nichts gefunden. Also habe ich mich gefragt, ob es von etwas anderem gemacht wird, also denke ich, dass APUE Recht hat.

    – John Zeng

    27. Dezember 2016 um 6:43 Uhr

Ich habe keine sehr genaue Antwort auf Ihre Frage gefunden, also habe ich beschlossen, eine weitere hinzuzufügen:

  1. Erstens über den Verlust von Daten, die Verwendung von Schreib- oder mmap/memcpy-Mechanismen schreibt sowohl in den Seitencache als auch wird vom Betriebssystem basierend auf seinen Seitenersetzungseinstellungen/-algos im Hintergrund mit dem zugrunde liegenden Speicher synchronisiert. Zum Beispiel hat Linux vm.dirty_writeback_centisecs, das bestimmt, welche Seiten als “alt” gelten und auf die Festplatte geleert werden. Selbst wenn Ihr Prozess nach erfolgreichem Schreibaufruf abbricht, gehen die Daten nicht verloren, da die Daten bereits in Kernelseiten vorhanden sind, die schließlich in den Speicher geschrieben werden. Der einzige Fall, in dem Sie Daten verlieren würden, ist, wenn das Betriebssystem selbst abstürzt (Kernelpanik, Ausschalten usw.). Der Weg, um absolut sicherzustellen, dass Ihre Daten den Speicher erreicht haben, wäre der Aufruf von fsync oder msync (für mmappte Regionen), je nach Fall.
  2. Was die Systemlast betrifft, ja, das Aufrufen von msync/fsync für jede Anfrage wird Ihren Durchsatz drastisch verlangsamen, also tun Sie das nur, wenn Sie müssen. Denken Sie daran, dass Sie sich wirklich vor Datenverlust bei Betriebssystemabstürzen schützen, was meiner Meinung nach selten ist und wahrscheinlich etwas, mit dem die meisten leben könnten. Eine allgemeine Optimierung besteht darin, in regelmäßigen Abständen, z. B. 1 Sekunde, eine Synchronisierung durchzuführen, um eine gute Balance zu erhalten.

Entweder sind die Informationen auf der Linux-Manpage falsch oder Linux ist schrecklich nicht konform. msync soll nichts damit zu tun haben, ob die Änderungen auf den logischen Zustand der Datei übertragen werden oder ob andere Prozesse dies verwenden mmap oder read um auf die Datei zuzugreifen, sehen Sie sich die Änderungen an; es ist ein reines Analogon von fsync und sollte als No-Op behandelt werden, außer um die Datenintegrität im Falle eines Stromausfalls oder eines anderen Ausfalls auf Hardwareebene sicherzustellen.

  • Nun, munmap (oder besser gesagt die entsprechende Funktion des Kernels flush_whatsitcalled) wird in beiden Fällen aufgerufen, wenn der Prozess beendet wird, also gibt es nicht wirklich ein Problem, oder? Es wäre verheerend, wenn Mappings nicht auf die Festplatte geschrieben würden, wenn ein Prozess stirbt, denn nicht wenige Programme (darunter einige, die ich geschrieben habe) verlassen sich darauf.

    – Dämon

    5. Mai 2011 um 22:19 Uhr

  • Beachten Sie auch, dass die freigegebenen Seiten von zugeordnet werden mmap werden mit dem Dateisystem-Cache geteilt, daher sehe ich keine Möglichkeit, dass es zu Inkonsistenzen oder Datenverlust kommen könnte. Ich denke, es würde eine absichtlich kaputte Implementierung erfordern, um Daten zu verlieren (wofür uClinux/NOMMU ein Beispiel ist :).

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    5. Mai 2011 um 22:26 Uhr

Benutzer-Avatar
John Ledbetter

Laut Manpage,

Die Datei wird möglicherweise erst aktualisiert, wenn msync(2) oder munmap() aufgerufen wird.

Sie müssen also sicherstellen, dass Sie anrufen munmap() zumindest vor dem Ausstieg.

  • Nun, munmap (oder besser gesagt die entsprechende Funktion des Kernels flush_whatsitcalled) wird in beiden Fällen aufgerufen, wenn der Prozess beendet wird, also gibt es nicht wirklich ein Problem, oder? Es wäre verheerend, wenn Mappings nicht auf die Festplatte geschrieben würden, wenn ein Prozess stirbt, denn nicht wenige Programme (darunter einige, die ich geschrieben habe) verlassen sich darauf.

    – Dämon

    5. Mai 2011 um 22:19 Uhr

  • Beachten Sie auch, dass die freigegebenen Seiten von zugeordnet werden mmap werden mit dem Dateisystem-Cache geteilt, daher sehe ich keine Möglichkeit, dass es zu Inkonsistenzen oder Datenverlust kommen könnte. Ich denke, es würde eine absichtlich kaputte Implementierung erfordern, um Daten zu verlieren (wofür uClinux/NOMMU ein Beispiel ist :).

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    5. Mai 2011 um 22:26 Uhr

1380250cookie-checkmmap-, msync- und Linux-Prozessbeendigung

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

Privacy policy