2012-01-02 17 views
7

Ho bisogno di una ridimensionazione senza copia di un file mmap di grandi dimensioni pur consentendo l'accesso simultaneo ai thread del lettore.Ridimensionamento rapido di un file mmap

Il modo semplice è utilizzare due mapping MAP_SHARED (far crescere il file, quindi creare una seconda mappatura che include la regione di sviluppo) nello stesso processo sullo stesso file e quindi rimuovere la mappatura precedente una volta che tutti i lettori che potevano accedervi sono finiti. Tuttavia, sono curioso di sapere se lo schema qui sotto potrebbe funzionare, e se è così, c'è qualche vantaggio ad esso.

  1. mmap un file con MAP_PRIVATE
  2. do accesso in sola lettura a questa memoria in più thread
  3. o acquisire un mutex per il file, scrivere alla memoria (assumere questo è fatto in modo che il i lettori, che possono leggere quella memoria, non ne sono incasinati)
  4. o acquisire il mutex, ma aumentare la dimensione del file e usare mremap per spostarlo in un nuovo indirizzo (ridimensionare la mappatura senza copiare o file non necessario IO.)

La parte pazza arriva a (4). Se si sposta la memoria, i vecchi indirizzi non sono più validi e i lettori, che stanno ancora leggendo, potrebbero improvvisamente avere una violazione di accesso. Cosa succede se modifichiamo i lettori per intercettare questa violazione di accesso e quindi riavviare l'operazione (cioè non rileggere l'indirizzo errato, ricalcolare l'indirizzo dato l'offset e il nuovo indirizzo di base da mremap.) Sì, lo so che è malvagio , ma a mio avviso i lettori possono solo leggere correttamente i dati al vecchio indirizzo o fallire con una violazione di accesso e riprovare. Se si presta sufficiente attenzione, che dovrebbe essere sicuro. Dal momento che il ridimensionamento non accadrebbe spesso, i lettori alla fine riuscirebbero e non rimanere bloccati in un ciclo di tentativi.

Un problema può verificarsi se tale vecchio spazio indirizzo viene riutilizzato mentre un lettore ha ancora un puntatore ad esso. Quindi non ci saranno violazioni dell'accesso, ma i dati non saranno corretti e il programma entrerà nell'unicorno e nella terra piena di caramelle di comportamento indefinito (in cui di solito non vi sono né unicorni né caramelle.)

Ma se hai controllato completamente le allocazioni e potresti assicurarsi che tutte le allocazioni che si verificano durante questo periodo non riutilizzino mai quel vecchio spazio indirizzo, quindi questo non dovrebbe essere un problema e il comportamento non dovrebbe essere indefinito.

Ho ragione? Potrebbe funzionare? C'è qualche vantaggio su questo usando due mappature MAP_SHARED?

+0

Si potrebbe semplicemente usare un blocco di lettura/scrittura e proteggere il remapping sotto il blocco di scrittura, no? – fge

+0

Direi che il collo di bottiglia è il disco. Sei sicuro che ne valga la pena? copiare file enormi da disco richiede sempre tempo, perché i dischi sono dispositivi meccanici lenti. –

+0

fge, sì ma il blocco sui thread di lettura è fuori questione in questo caso – Eloff

risposta

4

È difficile per me immaginare un caso in cui non si conosce il limite superiore della dimensione del file. Supponendo che sia vero, è possibile "riservare" lo spazio degli indirizzi per la dimensione massima del file fornendo tale dimensione quando il file viene mappato per la prima volta con mmap(). Ovviamente, qualsiasi accesso oltre la dimensione effettiva del file causerà una violazione di accesso, ma è così che si desidera che funzioni comunque: si potrebbe obiettare che riservare lo spazio di indirizzi aggiuntivo assicura la violazione di accesso anziché lasciare quell'intervallo di indirizzi aperto ad essere utilizzato da altre chiamate a cose come mmap() o malloc().

Comunque, il punto è con la mia soluzione, non avete mai mossa l'intervallo di indirizzi, si cambia solo la sua dimensione e ora il tuo blocco è intorno alla struttura di dati che fornisce la dimensione corrente valido per ogni thread.

La mia soluzione non funziona se si dispone di così tanti file che la mappatura massimo per ogni file che si esaurisce lo spazio degli indirizzi, ma questa è l'età dello spazio di indirizzamento a 64 bit quindi speriamo che la dimensione massima di mappatura non è problema.

(Giusto per assicurarmi che non stavo dimenticando qualcosa di stupido, ho scritto un piccolo programma per convincere me stesso a creare una mappatura di dimensioni maggiori di file dà una violazione di accesso quando si tenta di accedere oltre la dimensione del file, e poi funziona bene una volta ftruncate() il file da ingrandire, tutto con lo stesso indirizzo restituito dalla prima chiamata mmap().)

+0

Questa è stata la mia prima idea, ma purtroppo lo spazio degli indirizzi è limitato a 8 TB nei moderni sistemi operativi, il che significa che se ci sono molti file mappati in memoria che riservano il massimo spazio possibile, è possibile che si esaurisca lo spazio degli indirizzi. Uno potrebbe usare mmap con MAP_NORESERVE per riservare quanto più spazio di indirizzamento possibile all'avvio del programma, quindi allocare da lì con MAP_FIXED, distribuirlo in modo uniforme all'inizio e quindi prelevare dalle mappe che lo utilizzano meno quando si esaurisce. Quindi usa la strategia sopra o memcpy standard se un mmap esaurisce lo spazio e non c'è spazio adiacente riservato. – Eloff

+0

da dove prendi la tua cifra da 8TB? – camelccc

+2

Linux può gestire 64 TB di memoria phsyiscal all'interno di uno spazio di indirizzamento di 128 TB. Non sono sicuro che tu abbia molti file più grandi di 128 TB (anche il volume RAID completo sulla mia condivisione di rete è di soli 24 TB, ma il tuo chilometraggio può variare). Non ti capita di essere Google, vero? – Damon