Non ho trovato una risposta molto precisa alla tua domanda, quindi ho deciso di aggiungerne un'altra:
- In primo luogo sulla perdita di dati, utilizzando i meccanismi di scrittura o mmap/memcpy entrambi scrivono nella cache della pagina e vengono sincronizzati con l'archiviazione sottostante in background dal sistema operativo in base alle impostazioni/algo di sostituzione della pagina. Ad esempio Linux ha vm.dirty_writeback_centisecs che determina quali pagine sono considerate "vecchie" da scaricare su disco. Ora, anche se il tuo processo muore dopo che la chiamata di scrittura ha avuto successo, i dati non andrebbero persi poiché i dati sono già presenti nelle pagine del kernel che alla fine verranno scritte nell'archivio. L'unico caso in cui potresti perdere i dati è se il sistema operativo stesso si arresta in modo anomalo (panico del kernel, spegnimento, ecc.). Il modo per assicurarti assolutamente che i tuoi dati abbiano raggiunto l'archiviazione sarebbe chiamare fsync o msync (per le regioni mappate) a seconda dei casi.
- Per quanto riguarda il problema del carico di sistema, sì, chiamare msync/fsync per ogni richiesta rallenterà drasticamente il tuo throughput, quindi fallo solo se necessario. Ricorda che stai davvero proteggendo dalla perdita di dati in caso di arresti anomali del sistema operativo che presumo sia raro e probabilmente qualcosa con cui la maggior parte potrebbe convivere. Un'ottimizzazione generale eseguita consiste nell'emettere la sincronizzazione a intervalli regolari, ad esempio 1 secondo, per ottenere un buon equilibrio.
Ho trovato un commento di Linus Torvalds che risponde a questa domandahttp://www.realworldtech.com/forum/?threadid=113923&curpostid=114068
Le pagine mappate fanno parte della cache del filesystem, il che significa che anche se il processo utente che ha apportato una modifica a quella pagina muore, la pagina è ancora gestita dal kernel e poiché tutti gli accessi simultanei a quel file passeranno attraverso il kernel, altri i processi verranno serviti da quella cache. In alcuni vecchi kernel Linux era diverso, questo è il motivo per cui alcuni documenti del kernel dicono ancora di forzare msync
.
EDIT:Grazie, RobH ha corretto il link.
MODIFICA:
A partire da Linux 4.15 viene introdotto un nuovo flag, MAP_SYNC, che può garantire la coerenza.
Le mappature dei file condivisi con questo flag forniscono la garanzia che, sebbene parte della memoria sia mappata in modo scrivibile nello spazio degli indirizzi del processo, sarà visibile nello stesso file allo stesso offset anche dopo che il sistema si è bloccato o è stato riavviato.
riferimenti:
http://man7.org/linux/man-pages/man2/mmap.2.html cerca MAP_SYNC nella pagina
https://lwn.net/Articles/731706/
Ho deciso di essere meno pigro e di rispondere alla domanda se i dati vengono scritti definitivamente su disco scrivendo del codice. La risposta è che verrà scritto.
Ecco un programma che si interrompe bruscamente dopo aver scritto alcuni dati in un file mmap'd:
#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);
}
Ecco un programma che convalida il file risultante dopo che il programma precedente è morto:
#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");
}
Ho trovato qualcosa che si aggiunge alla mia confusione:
munmap non influisce sull'oggetto che è stato mappato, ovvero la chiamata a munmap non causa la scrittura del contenuto della regione mappata nel file su disco . L'aggiornamento del file su disco per una regione MAP_SHARED avviene automaticamente dall'algoritmo di memoria virtuale del kernel mentre memorizziamo nella regione mappata in memoria.
questo è tratto da Programmazione avanzata nell'ambiente UNIX® .
dalla manpage di Linux:
MAP_SHARED Condividi questa mappatura con tutti gli altri processi che mappano questo oggetto. La memorizzazione nella regione equivale alla scrittura nel file. Il file potrebbe non essere effettivamente aggiornato finché non vengono richiamati msync(2) o munmap(2).
i due sembrano contraddittori. APUE è sbagliato?