mmap - Mappatura di File in Memoria
Scopo: Mappa file in memoria invece di leggerne direttamente il contenuto
La mappatura in memoria di un file avviene tramite l'utilizzo del sistema di memoria virtuale del sistema operativo per accedere direttamente ai dati in un file nel filesystem, invece di utilizzare le normali funzioni di I/O. In genere le prestazioni di I/O sono migliorate con questa tecnica visto che non occorre eseguire chiamate di sistema separate per ogni accesso e non occorre copiare dati tra buffer - la memoria è indirizzata direttamente sia dal kernel che dall'applicazione dell'utente.
I file mappati in memoria possono essere trattati come stringhe modificabili oppure oggetti di tipo file, a seconda delle necessità. Un file mappato supporta i previsti metodi API, tipo close()
, flush()
, read()
, readline()
, seek()
, tell()
e write()
. Supporta anche le API delle stringhe, con caratteristiche come lo slicing (il trattare porzioni di iterabili) e metodi come find()
.
Tutti gli esempi usano il file lorem.txt
di seguito stampato:
Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
Donec egestas, enim et consectetuer ullamcorper, lectus ligula rutrum leo,
a elementum elit tortor eu quam. Duis tincidunt nisi ut ante. Nulla
facilisi. Sed tristique eros eu libero. Pellentesque vel
arcu. Vivamus purus orci, iaculis ac, suscipit sit amet, pulvinar eu,
lacus. Praesent placerat tortor sed nisl. Nunc blandit diam egestas
dui. Pellentesque habitant morbi tristique senectus et netus et
malesuada fames ac turpis egestas. Aliquam viverra fringilla
leo. Nulla feugiat augue eleifend nulla. Vivamus mauris. Vivamus sed
mauris in nibh placerat egestas. Suspendisse potenti. Mauris
massa. Ut eget velit auctor tortor blandit sollicitudin. Suspendisse
imperdiet justo.
mmap()
tra Unix e Windows, che non sono interamente qui trattati. Per maggiori dettagli, fare riferimento alla documentazione della libreria standard.Lettura
Si usi la funzione mmap()
per creare un file mappato in memoria. Il primo argomento è un descrittore di file, ottenuto dal metodo fileno()
oppure da os.open()
oppure un oggetto file
. Il chiamante è responsabile dell'apertura del file prima della chiamata di mmap()
e della chiusura quando lo stesso non è più necessario.
Il secondo argomento per mmap()
è la dimensione in byte della porzione del file da mappare. Se il valore è 0
viene mappato l'intero file. Se la dimensione è maggiore della dimensione corrente del file, il file viene esteso.
Un argomento nominale opzionale è access
, supportato da entrambe le piattaforme. Si usi ACCESS_READ
per accesso a sola lettura, ACCESS_WRITE
per scrittura (gli assegnamenti alla memoria vanno direttamente nel file) ed ACCESS_COPY
per copia in scrittura (gli assegnamenti alla memoria non sono scritti nel file).
# mmap_read.py
import mmap
with open('lorem.txt', 'r') as f:
with mmap.mmap(f.fileno(), 0,
access=mmap.ACCESS_READ) as m:
print('Primi 10 byte tramite read :', m.read(10))
print('Primi 10 byte tramite slice:', m[:10])
print('Secondi 10 byte tramite read :', m.read(10))
Il puntatore al file traccia gli ultimi byte acceduti tramite una operazione di slicing. In questo esempio, il puntatore si sposta in avanti di 10 byte dopo la prima lettura. Poi viene reimpostato a inizio file dall'operazione di slicing e successivamente spostato ancora di 10 byte, ancora dallo slicing. Dopo questa operazione, la chiamata di read()
ritorna i byte compresi nelle posizioni da 11 a 20 nel file.
$ python3 mmap_read.py Primi 10 byte tramite read : b'# lorem.tx' Primi 10 byte tramite slice: b'# lorem.tx' Secondi 10 byte tramite read : b't\n\nLorem i'
Scrittura
Per impostare un file mappato in memoria affinchè possa ricevere aggiornamenti, si inizi con lo specificare la modalità di apertura in 'r+'
, (non 'w'
) prima di mapparlo. Poi è possibile usare un qualunque metodo che modifichi i dati (write()
, assegnazioni tramite slicing ecc.).
Il successivo esempio usa la modalità di accesso predefinita ACCESS_WRITE
e con una operazione di slicing modifica sul posto parte di una riga.
# mmap_write_slice.py
import mmap
import shutil
# Copia il file di esempio
shutil.copyfile('lorem.txt', 'lorem_copy.txt')
word = b'consectetuer'
reversed = word[::-1]
print('Ricerca di :', word)
print('Da sostituire con :', reversed)
with open('lorem_copy.txt', 'r+') as f:
with mmap.mmap(f.fileno(), 0) as m:
print('Prima:\n{}'.format(m.readline().rstrip()))
m.seek(0) # torna a inizio file
loc = m.find(word)
m[loc:loc + len(word)] = reversed
m.flush()
m.seek(0) # torna a inizio file
print('Dopo :\n{}'.format(m.readline().rstrip()))
f.seek(0) # torna a inizio file
print('File :\n{}'.format(f.readline().rstrip()))
La parola consectetuer
viene sostituita nella prima riga in memoria e nel file.
$ python3 mmap_write_slice.py Ricerca di : b'consectetuer' Da sostituire con : b'reutetcesnoc' Prima: b'Lorem ipsum dolor sit amet, consectetuer adipiscing elit.' Dopo : b'Lorem ipsum dolor sit amet, reutetcesnoc adipiscing elit.' File : Lorem ipsum dolor sit amet, reutetcesnoc adipiscing elit.
Modalità Copia
Con l'impostazione di accesso ACCESS_COPY
il file su disco non viene modificato.
# mmap_write_copy.py
import mmap
import shutil
# Copy the example file
shutil.copyfile('lorem.txt', 'lorem_copy.txt')
word = b'consectetuer'
reversed = word[::-1]
with open('lorem_copy.txt', 'r+') as f:
with mmap.mmap(f.fileno(), 0,
access=mmap.ACCESS_COPY) as m:
print('Prima in memoria:\n{}'.format(
m.readline().rstrip()))
print('Dopo nel file :\n{}\n'.format(
f.readline().rstrip()))
m.seek(0) # torna a inizio file
loc = m.find(word)
m[loc:loc + len(word)] = reversed
m.seek(0) # torna a inizio file
print('Prima in memoria :\n{}'.format(
m.readline().rstrip()))
f.seek(0)
print('Dopo nel file :\n{}'.format(
f.readline().rstrip()))
In questo esempio è necessario riportare il puntatore all'inizio del file sia nella versione su disco che su quella in memoria in quanto lo stato interno dei due oggetti viene mantenuto separatamente.
$ python3 mmap_write_copy.py Prima in memoria: b'Lorem ipsum dolor sit amet, consectetuer adipiscing elit.' Dopo nel file : Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Prima in memoria : b'Lorem ipsum dolor sit amet, reutetcesnoc adipiscing elit.' Dopo nel file : Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
Espressioni Regolari
Visto che un file mappato in memoria può agire come se fosse una stringa, può essere usato con altri moduli che operano con stringhe tipo le espressioni regolari. Questo esempio trova tutte le frasi che contengono "nulla"
.
# mmap_regex.py
import mmap
import re
pattern = re.compile(rb'(\.\W+)?([^.]?nulla[^.]*?\.)',
re.DOTALL | re.IGNORECASE | re.MULTILINE)
with open('lorem.txt', 'r') as f:
with mmap.mmap(f.fileno(), 0,
access=mmap.ACCESS_READ) as m:
for match in pattern.findall(m):
print(match[1].replace(b'\n', b' '))
Visto che il modello di ricerca comprende due gruppi, il valore ritornato da findall()
è una sequenza di tuple. La funzione print()
estrae le frasi che hanno trovato corrispondenza e sostituisce i ritorni a capo con spazi in modo che ciascun risultato venga stampato in una sola riga.
$ python3 mmap_regex.py b'Nulla facilisi.' b'Nulla feugiat augue eleifend nulla.'
Vedere anche:
- mmap
- La documentazione della libreria standard per questo modulo
- Pattern Matching: The Gestalt Approach
- Discussione su di un algoritmo simile di John W. Ratcliff e D. E. Metzener pubblicato nel Dr. Dobb's Journal del luglio 1988 (in inglese).