L'utilizzo di file per la memoria condivisa IPC, la mappatura della memoria è un requisito?

voti
19

Ci sono alcuni progetti che usano i MappedByteBuffers restituiti da FileChannel.map() di Java come un modo per avere una memoria condivisa IPC tra JVM sullo stesso host (vedi Chronicle Queue, Aeron IPC, ecc.). Per quanto ne so, questa api si trova proprio sopra la chiamata mmap. Tuttavia, l'implementazione di Java non permette mappature anonime (non supportate da file).

La mia domanda è: su Java (1.8) e Linux (3.10), i MappedByteBuffer sono davvero necessari per l'implementazione della memoria condivisa IPC, o l'accesso ad un file comune fornirebbe le stesse funzionalità? (Questa domanda non riguarda le implicazioni in termini di prestazioni dell'utilizzo di un MappedByteBuffer o meno)

Ecco la mia comprensione:

  1. Quando Linux carica un file da disco, copia il contenuto di quel file nelle pagine in memoria. Quella regione della memoria è chiamata cache delle pagine. Per quanto ne so, lo fa indipendentemente dal metodo Java (FileInputStream.read(), RandomAccessFile.read(), FileChannel.read(), FileChannel.read(), FileChannel.map()) o dal metodo nativo che viene usato per leggere il file (osservato con free e monitorando il valore di cache).
  2. Se un altro processo tenta di caricare lo stesso file (mentre è ancora residente nella cache) il kernel lo rileva e non ha bisogno di ricaricare il file. Se la cache delle pagine si riempie, le pagine vengono sfrattate - quelle sporche vengono riscritte sul disco. (Le pagine vengono anche riscritte se c'è un esplicito flush su disco, e periodicamente, con un thread del kernel).
  3. Avere un file (grande) già presente nella cache è un significativo incremento delle prestazioni, molto più delle differenze basate sui metodi Java che usiamo per aprire/leggere quel file.
  4. Un programma C che chiama la chiamata di sistema mmap può fare una mappatura ANONIMA, che essenzialmente alloca nella cache pagine che non sono supportate da un file reale (quindi non c'è bisogno di emettere scritture reali sul disco), ma Java non sembra offrire questo (mappare un file in tmpfs farebbe la stessa cosa?)
  5. Se un file viene caricato usando la chiamata di sistema mmap (C) o tramite FileChannel.map() (Java), essenzialmente le pagine del file (nella cache) vengono caricate direttamente nello spazio degli indirizzi del processo. Usando altri metodi per aprire un file, il file viene caricato in pagine non nello spazio degli indirizzi del processo, e poi i vari metodi di lettura/scrittura di quel file copiano alcuni byte da/verso quelle pagine in un buffer nello spazio degli indirizzi del processo. C'è un ovvio vantaggio in termini di prestazioni che evita quella copia, ma la mia domanda non riguarda le prestazioni.

Quindi, in sintesi, se ho capito bene - mentre il mapping offre un vantaggio in termini di prestazioni, non sembra offrire alcuna funzionalità di memoria condivisa che non abbiamo già ottenuto solo dalla natura di Linux e dalla cache delle pagine.

Quindi, per favore, fatemi sapere dove è la mia comprensione.

Grazie.

È pubblicato 22/05/2020 alle 21:20
fonte dall'utente
In altre lingue...                            


2 risposte

voti
0

Vale la pena di menzionare tre punti: performance, e cambiamenti simultanei, e utilizzo della memoria.

È corretto nella valutazione che l'MMAP-based di solito offre un vantaggio in termini di prestazioni rispetto all'IO basato su file. In particolare, il vantaggio di performance è significativo se il codice esegue molto poco IO nel punto artbitrario del file.

considerare la possibilità di cambiare l'N-esimo byte: con mmap buffer[N] = buffer[N] + 1, e con l'accesso basato su file è necessario (almeno) un controllo degli errori delle 4 chiamate di sistema:

   seek() + error check
   read() + error check
   update value
   seek() + error check
   write + error check

È vero che il numero di IO effettivi (sul disco) è molto probabilmente lo stesso.

Il secondo punto degno di nota è l'accesso simultaneo. Con l'IO basato su file, ci si deve preoccupare di un potenziale accesso simultaneo. Sarà necessario emettere un blocco esplicito (prima della lettura), e sbloccare (dopo la scrittura), per evitare due processi per accedere in modo errato al valore contemporaneamente. Con la memoria condivisa, le operazioni atomiche possono eliminare la necessità di un ulteriore blocco.

Il terzo punto è l'effettivo utilizzo della memoria. Nei casi in cui la dimensione degli oggetti condivisi è significativa, l'uso della memoria condivisa può consentire a un gran numero di processi di accedere ai dati senza allocare memoria aggiuntiva. Se sistemi vincolati dalla memoria, o sistemi che devono fornire prestazioni in tempo reale, questo potrebbe essere l'unico modo per accedere ai dati.

Risposto il 29/05/2020 a 10:35
fonte dall'utente

voti
0

La mia domanda è: su Java (1.8) e Linux (3.10), i MappedByteBuffer sono davvero necessari per implementare la memoria condivisa IPC, o l'accesso ad un file comune fornirebbe le stesse funzionalità?

Dipende dal motivo per cui si vuole implementare la memoria condivisa IPC.

È possibile implementare chiaramente l'IPC senza memoria condivisa; ad es. su prese. Quindi, se non lo si fa per motivi di performance, non è affatto necessario fare IPC a memoria condivisa!

Quindi le prestazioni devono essere alla base di ogni discussione.

L'accesso utilizzando i file tramite le API Java classic io o nio non fornisce funzionalità o prestazioni di memoria condivisa.

La differenza principale tra i normali file I/O o Socket I/O rispetto alla memoria condivisa IPC è che il primo richiede alle applicazioni di fare reade writesyscalls esplicitamente per inviare e ricevere messaggi. Questo comporta ulteriori syscalls, e comporta la copia dei dati del kernel. Inoltre, se ci sono più thread, è necessario un "canale" separato tra ogni coppia di thread o qualcosa per multiplexare più "conversazioni" su un canale condiviso. Quest'ultimo può portare al fatto che il canale condiviso diventi un collo di bottiglia della concorrenza.

Si noti che questi overhead sono ortogonali alla cache delle pagine di Linux.

Al contrario, con l'IPC implementato usando la memoria condivisa, non ci sono syscalls reade writesyscalls, e nessuna fase di copia extra. Ogni "canale" può semplicemente utilizzare un'area separata del buffer mappato. Un thread in un processo scrive i dati nella memoria condivisa ed è quasi immediatamente visibile al secondo processo.

L'avvertenza è che i processi devono 1) sincronizzarsi, e 2) implementare barriere di memoria per assicurare che il lettore non veda i dati stantii. Ma queste possono essere implementate entrambe senza chiamate di sicurezza.

Nel lavaggio, la memoria condivisa IPC che utilizza file mappati in memoria >> è<<< più veloce rispetto all'utilizzo di file o prese convenzionali, ed è per questo che la gente lo fa.


Avete anche chiesto implicitamente se l'IPC a memoria condivisa può essere implementato senza file mappati in memoria.

  • Un modo pratico sarebbe quello di creare un file mappato in memoria per un file che vive in un file system di sola memoria; per esempio un "tmpfs" in Linux.

    Tecnicamente, questo è ancora un file mappato in memoria. Tuttavia, non si incorrono in sovraccarichi di dati da scaricare su disco, e si evita la potenziale preoccupazione per la sicurezza dei dati IPC privati che finiscono su disco.

  • In teoria si potrebbe implementare un segmento condiviso tra due processi facendo quanto segue:

    • Nel processo genitore, usare mmap per creare un segmento con MAP_ANONYMOUS | MAP_SHARED.
    • Processi figlio a forcella. Questi finiranno per condividere il segmento tra loro e il processo dei genitori.

    Tuttavia, implementarlo per un processo Java sarebbe... impegnativo. AFAIK, Java non lo supporta.

Riferimento:

Risposto il 31/05/2020 a 06:17
fonte dall'utente

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more