2016-06-14 2 views
5

bearbeiten: Verwenden von Win10 und Python 3.5Python, warum füllt mmap.move() den Speicher auf?

ich eine Funktion haben, die mmap verwendet Bytes aus einer Datei zu einem bestimmten entfernen Offset:

def delete_bytes(fobj, offset, size): 
    fobj.seek(0, 2) 
    filesize = fobj.tell() 
    move_size = filesize - offset - size 

    fobj.flush() 
    file_map = mmap.mmap(fobj.fileno(), filesize) 
    file_map.move(offset, offset + size, move_size) 
    file_map.close() 

    fobj.truncate(filesize - size) 
    fobj.flush() 

Es super schnell funktioniert, aber wenn ich es auf eine Flucht große Anzahl von Dateien, der Speicher füllt sich schnell und mein System reagiert nicht mehr.

Nach einigen Experimenten habe ich festgestellt, dass die move() -Methode hier die Ursache war, und insbesondere die Menge der zu verschiebenden Daten (move_size). Die Menge des verwendeten Speichers entspricht der Gesamtmenge der Daten, die von mmap.move() verschoben werden. Wenn ich 100 Dateien mit je ~ 30 MB verschoben habe, wird der Speicher mit ~ 3GB gefüllt.

Warum werden die verschobenen Daten nicht aus dem Speicher freigegeben?

Dinge, die ich versuchte, die keine Wirkung hatte:

  • gc.collect() am Ende der Funktion aufrufen.
  • die Funktion umschreiben, um sich in kleine Stücke zu bewegen.
+0

Welches Betriebssystem verwenden Sie? Python-Version auch. – wind85

+0

Können Sie bitte auch überprüfen, ob der Speicher von Ihrem Python-Prozess oder vom Betriebssystem verwendet wird? – Leon

+0

Sorry, habe vergessen zu erwähnen: Ich bin auf Win10 und Python 3.5. Wie überprüfe ich, ob der Speicher von Python oder OS verwendet wird? – mahkitah

Antwort

1

Dies scheint sollte funktionieren. Ich habe ein verdächtiges Bit im Quellcode mmapmodule.c gefunden, #ifdef MS_WINDOWS. Insbesondere nachdem alle Setup-Argumente analysieren zu, wird der Code dann tut dies:

if (fileno != -1 && fileno != 0) { 
    /* Ensure that fileno is within the CRT's valid range */ 
    if (_PyVerify_fd(fileno) == 0) { 
     PyErr_SetFromErrno(PyExc_OSError); 
     return NULL; 
    } 
    fh = (HANDLE)_get_osfhandle(fileno); 
    if (fh==(HANDLE)-1) { 
     PyErr_SetFromErrno(PyExc_OSError); 
     return NULL; 
    } 
    /* Win9x appears to need us seeked to zero */ 
    lseek(fileno, 0, SEEK_SET); 
} 

, die sich bewegt zugrunde liegende Offset-Dateiobjekt von „Ende der Datei“ auf „Start der Datei“ und dann verlässt es dort. Das scheint sollte nicht etwas zu brechen, aber es könnte sich lohnen, Ihre eigenen Start-of-File-Datei kurz vor dem Aufruf mmap.mmap zu tun, um die Datei zuzuordnen.

(Alles unterhalb falsch ist, aber links in da es Kommentare dazu.)


In der Regel nach mmap() verwenden, müssen Sie munmap() verwenden, um die Zuordnung rückgängig zu machen. Das Schließen des Dateideskriptors hat keine Auswirkungen. Die Linux documentation Anrufe dies ausdrücklich aus:

munmap()
Der munmap() Systemaufruf löscht die Zuordnungen für den angegebenen Adressbereich, und verursachen weitere Verweise auf Adressen im Bereich ungültige Speicherreferenzen zu erzeugen. Die Region wird automatisch auch nicht zugeordnet, wenn der Prozess beendet wird. Auf der anderen Seite hebt das Schließen des Dateideskriptors die Region nicht auf.

(Die BSD-Dokumentation ist ähnlich. Windows-hier anders aus Unix-ähnlichen Systemen verhalten, aber was Sie sehen, deutet darauf hin, dass sie die gleiche Art und Weise funktionieren.)

Leider Python Mmap Modul nicht bindet der Systemaufruf (noch mprotect), zumindest ab 2.7.11 und 3.4.4. Als Workaround können Sie das Modul ctypes verwenden.Ein Beispiel finden Sie unter this question (es ruft reboot auf, aber die gleiche Technik funktioniert für alle C-Bibliotheksfunktionen). Oder, für eine etwas schönere Methode, können Sie Wrapper in schreiben.

+0

Führt 'mmap.close()' 'unmap()' nicht darunter aus? – Leon

+1

'mmap.close()' ruft 'UnmapViewOfFile' (Windows) oder' munmap' (Unix) auf (Python 3.4, mmapmodule.c). –

+0

Das Mapping selbst ist kein Problem. Wenn ich die Zeile mit 'mmap.move()' entferne oder sie durch eine andere Methode (wie 'mmap.resize()') ersetze, gibt es überhaupt kein Problem. – mahkitah

Verwandte Themen