2016-03-29 6 views
0

Ich programmiere auf Ubuntu, mit Python 2.7.3.Python CFFI Speicherverwaltungsprobleme

Ich verwende CFFI, um eine Python-Liste mit Werten aus C-Code zu füllen.
Diese Liste ist ziemlich groß: rund 71 000 Zeichen lang, wenn gedruckt.

Der C-Code verwendet viele Bibliotheken. Daher dient der folgende Code nur zum besseren Verständnis dessen, was gerade passiert.

datas_list = [] 
for i in range(0, x): 
    c_pDataStructure = ffi.new("c_DataStructure[]", 1) // Create a pointer to the data structure 
    c.SomeCFunction(c_pDataStructure) // Populate the data structure 
    datas_list.append(c.GetSomeInfo(c_pDataStructure)) // Get some info from the data structure 
    c.FreeDataStructure(c_pDataStructure) // Release dynamically allocated memory 

Das Programm läuft gut Wingware IDE aber endet mit einem glibc Fehler (*** glibc detected *** python: free(): invalid next size (fast): 0x0000000003b0b080 ***), wenn von der Kommandozeile, direkt vor gestartet:

c_pDataStructure = ffi.new("c_Datastructure[]", 1) 

Nach der Lektüre wims answer, überprüfte ich, wenn sowohl die IDE und die Befehlszeile führten den Code mit dem gleichen Interpreter aus - sie sind (/usr/bin/python).

EDIT (valgrind Bericht):

==5089== Process terminating with default action of signal 11 (SIGSEGV) 
==5089== General Protection Fault 
==5089== at 0x54FBB0: PyObject_Malloc (in /usr/bin/python2.7) 
==5089== by 0x10B30625: allocate_owning_object (_cffi_backend.c:2972) 
==5089== by 0x10B40EE8: allocate_with_allocator.constprop.84 (_cffi_backend.c:3032) 
==5089== by 0x10B41010: direct_newp (_cffi_backend.c:3153) 
==5089== by 0x10B4138C: b_newp (_cffi_backend.c:3177) 
==5089== by 0x4F95A4: PyEval_EvalFrameEx (in /usr/bin/python2.7) 
==5089== by 0x5008C1: PyEval_EvalCodeEx (in /usr/bin/python2.7) 
==5089== by 0x4F9AB7: PyEval_EvalFrameEx (in /usr/bin/python2.7) 
==5089== by 0x4F9D01: PyEval_EvalFrameEx (in /usr/bin/python2.7) 
==5089== by 0x4F9D01: PyEval_EvalFrameEx (in /usr/bin/python2.7) 
==5089== by 0x4F9D01: PyEval_EvalFrameEx (in /usr/bin/python2.7) 
==5089== by 0x4F9D01: PyEval_EvalFrameEx (in /usr/bin/python2.7) 

EDIT:
Hier sind einige weitere Informationen über das C-Datenstruktur. Dies ist, wie es aussieht:

typedef struct _STRUCT3{ 
    some int, char* 
}STRUCT3, *PSTRUCT3; 

typedef struct _STRUCT2{ 
    some int 
    PSTRUCT3 pStruct3; 
}STRUCT3, *PSTRUCT3; 

typedef struct _STRUCT1{ 
    some int, char* 
    PSTRUCT2 pStruct2; 
}STRUCT1, *PSTRUCT1; 

Ich machte ein kleines C-Programm Zuweisen/Frei eine vollständige C-Struktur und valgrind kein Speicherleck gefunden.

Fragen:

  • Was bedeutet die oben valgrind Bericht bedeutet das genau?
  • Was könnten die Unterschiede zwischen dem Ausführen des Programms von der IDE und von der Befehlszeile sein?
    Hinweis: Die IDE verwendet das Python-Argument -u (unbuffered), um das Programm auszuführen, aber das Hinzufügen zur Befehlszeile macht keinen Unterschied.
  • Wenn ich die Struktur selbst auflösche, handelt Pythons Garbage Collector? Sollte ich stattdessen ffi.gc(c_pDataStructure, c.FreeDataStructure) verwenden?
+0

'FreeDataStructure (p)' sollte den Zeiger '' p'' selbst nicht freigeben, sondern nur jedes dynamisch zugewiesene Material. Ich denke, das ist der Fall? –

+0

Ja, FreeDataStructure gibt alles frei, was die Populationsfunktion dynamisch zuweist – DRz

+0

Das Programm stürzt ab, nachdem eine Datenstruktur mit einem optionalen Member zum 33. Mal zugewiesen wurde. Daher vermute ich, dass der optionale Wert nicht ordnungsgemäß freigegeben wird. Ist Python (oder CFFI oder C?) Auf einen "32-Speicheradressbereich" geschrumpft, bevor die zuvor verwendete Speicheradresse überschrieben wurde? Dann, wenn Wingware das Programm auf einem 64-Bit-Python und die Befehlszeile auf einem 32-Bit-Python ausführt, könnte es erklären, warum letzteres in einem Glibc-Fehler endet ... Ich untersuche dieses – DRz

Antwort

0

fand ich, wie meine Probleme zu beheben:

I ffi.gc(cdata, destructor) verwendet, um die Struktur zu schaffen.Mein Python-Code sieht jetzt aus wie:

data_list = [] 
for i in range(0, x): 
    # Create a pointer to the data structure and tell the garbage collector how to destroy it 
    gc_c_pDataStructure = ffi.gc(c.CreateDataStructure(), c.FreeDataStructure) 
    c.SomeCFunction(gc_c_pDataStructure) # Populate the data structure 
    datas_list.append(c.GetSomeInfo(gc_c_pDataStructure)) # Store some data 

Hier sind einige Links mit Bezug zu ffi.gc():

Und hier ist die C-Funktion der Datenstruktur zu erstellen (nach dem Strukturbeispiel aus der Frage):

PSTRUCT1 CreateDataStructure() 
{ 
    PSTRUCT1 pStruct1 = (PSTRUCT1) malloc(sizeof(STRUCT1)); 
    _SetDummyValues(pStruct1); 

    return pStruct1; 
} 

Wie Sie sehen können, musste ich die Funktion void _SetDummyValues(PSTRUCT1 pStruct1) erstellen. Diese Funktion setzt die angegebenen Strukturzeiger auf NULL.

+1

Beachten Sie einen Unterschied: 'c.FreeDataStructure (p)' sollte im ursprünglichen Code nicht 'free (p)' heißen, da dies automatisch nach 'p = ffi.new()' geschieht. In dem modifizierten Code hier muss 'c.FreeDataStructure (p)' 'free (p)' entsprechend 'c.CreateDataStructure()' aufrufen 'malloc()' nennen. Wenn nicht, haben Sie ein Leck. –

+1

Auf der anderen Seite, wenn es 'free (p)' nennt und immer tat, dann haben wir gerade den ursprünglichen Grund für den Absturz herausgefunden --- double frees! –

+0

Ich erinnere mich, dass ich 'free (p)' löschen musste, bevor ich 'ffi.gc()' benutzte, weil das tatsächlich einen doppelten freien Effekt verursachte. Und du hast Recht, mein Programm war dann undicht, weil ich vergessen habe, diese Zeile erneut hinzuzufügen. Vielen Dank! – DRz

0

auch auf diesen Fehler in Zusammenhang gebracht haben Dies kann die Coverity in einigen unsere CFFI generierten Code gefunden:

x0 = (void *)alloca((size_t)datasize); 
    ... 
    { 
     free(x0); 
    } 

Wie Sie sehen können, ist es frei auf dem Stapel zugewiesene Speicher aufrufen.