malloc unter FreeBSD und Linux

Sickboy

Müßiggänger
Hallo,

gibt es eigentlich Unterschiede, wie FreeBSD und Linux malloc behandeln? Hatte einen Bug-Report reinbekommen: malloc/_int_free() führt unter Linux zu einem Segmentation Fault, weil der Speicher schon dealloziert wurde. Unter FreeBSD 12 lässt sich das aber nicht reproduzieren.

Woran kann das liegen?
 
malloc/_int_free() führt unter Linux zu einem Segmentation Fault,
Wenn du erklärst, was genau du mit _int_free() meinst, kann man dabei vielleicht helfen.

Konzeptionell funktioniert malloc() überall gleich, auch wenn die Implementierung sehr unterschiedlich sein kann. Letzteres sollte dich als Programmierer aber in der Regel nicht interessieren. Es ist immer ein Fehler, Speicher, der schon freigegeben wurde, noch zu benutzen. Je nach Implementierung und äußerer Umstände kann das direkt auffallen oder eben nicht, aber so oder so ist das Programm falsch, es enthält "undefined behavior".

Um solche Fehler besser zu finden eignet sich Valgrind, oder auf Windows, DrMemory. Übrigens ist mir persönlich aufgefallen, dass Windows anscheinend besonders "aggressiv" Speicher freigibt bzw wiederverwendet -- entsprechende Bugs werden auf Windows sehr häufig direkt sichtbar, während das Programm auf Linux (und auch auf FreeBSD) öfter mal noch zu funktionieren scheint.

edit: mein beobachtetes Verhalten bezieht sich auf mit MinGW compilierte Programme, die die alte MSVCRT.DLL als standard C library linken. Gut möglich, dass das mit anderen Libraries (z.B. neueren Versionen von msvcrt, die mit Visual Studio verteilt werden) schon wieder anders aussieht. In der Regel arbeiten malloc() Implementierungen nämlich so, dass sie größere Bereiche vom OS anfordern (z.B. mittels mmap()) und diese dann selbst verwalten. Wie schnell dann ein Speicherbereich anderweitig verwendet wird, oder komplett an das OS zurückgegeben wird, hängt von der Strategie in der C Library ab. Deshalb kann es leicht passieren, dass ein fehlerhaftes Programm scheinbar korrekt läuft. Wenn man da unsicher ist, sollte man immer ein debugging tool nutzen, das alle Speicherzugriffe, -anforderungen und -freigaben trackt, siehe oben :)
 
Um solche Fehler besser zu finden eignet sich Valgrind, oder auf Windows, DrMemory.
Ein guter Tipp ist auch, Debug-Builds (auf denen man testet) immer und grundsätzlich mit Address Sanitizer zu bauen. Gibt es inzwischen auch für MSVC, wenn man damit gestraft ist.
 
Wegen solcher Fehler halte ich RAII für das Killerfeature von C++. Damit könnte man IMHO > 90% der Resource Leak Probleme beheben. Natürlich gibt es Dinge wo das nicht geht, etwa wenn man über eine async Schnittstelle Locks setzten lässt. Aber zumindest der Client kann RAII einsetzen und Server seitig können Timeouts abgestürzte Clients oder Verbindungsabbrüche behandeln.
 
Wegen solcher Fehler halte ich RAII für das Killerfeature von C++.
Also genaugenommen ist RAII kein "Feature" sondern ein dringend empfohlener Coding/Design-Stil, der notwendig ist, weil C++ Sprachunterstützung für OOP und sogar Exceptions mit explizitem Resourcenmanagement kombiniert. Man könnte es etwas bösartiger :D auch einen Workaround nennen.

Ich persönlich mache einen Bogen um C++ wenn ich kann -- verwende aber durchaus OOP auch in C und natürlich ist das RAII Konzept dann sinnvoll ;) -- wie du aber auch schon feststellst ist es leider nicht in jeder Situation anwendbar. Dann hilft nur, sich immer im klaren zu sein, wer ein Objekt "besitzt" (also fürs Aufräumen zuständig ist), und wann und wie so ein Besitz gegebenenfalls wechselt.
Ein guter Tipp ist auch, Debug-Builds (auf denen man testet) immer und grundsätzlich mit Address Sanitizer zu bauen. Gibt es inzwischen auch für MSVC, wenn man damit gestraft ist.
Habe ich tatsächlich nie verwendet, danke für den Tipp, das muss ich mir mal ansehen :)
 
Also genaugenommen ist RAII kein "Feature" sondern ein dringend empfohlener Coding/Design-Stil …
Der aber nur dadurch möglich ist, dass Finalisation (ein Feature das C generell fehlt) zu klar geregelten Zeitpunkten stattfindet, anders als bei den Garbage Collector Sprachen wie Java. C# hat extra irgendein keyword um Objekte dem GC zu entziehen um explizit für bestimmte Objekte wie File Instanzen deterministisches RAII zu ermöglichen. Ich denke man kann RAII schon ein Feature nennen, weil doch einiges zusammen kommen muss um RAII überhaupt erst zu ermöglichen.

Der Vorteil von RAII gegenüber GC ist halt, dass es für alle arten von Ressourcen funktioniert, nicht nur Speicher. Es ist auch in Echtzeitsystemen anwendbar (anders als Exceptions zum Beispiel) und es ist auch ohne Exceptions empfehlenswert es gibt da nämlich ein Sprachfeature mit dem Namen return das man erst damit ohne Kopfschmerzen einsetzen kann.
 
Am Ende war es wohl das -O2 flag beim Kompilieren. Unter FreeBSD lief alles stabil, unter Linux kam es zum Seg Fault. Mit -O0 aber keine Probleme.
-O2 führt aber keine Bugs ein, es macht sie nur unter Umständen sichtbar. Ich würde dir dringend empfehlen, das zu debuggen und reparieren.
 
Der compiler (GNU Fortran) scheint eher Probleme mit der Parallelisierung bei -O2 zu haben. Muss das bei Gelegenheit mal mit dem Intel compiler testen.
 
Bisher war ich von C oder C++ ausgegangen :D Bei "exotischen Sprachen" will ich natürlich nicht ausschließen, dass auch Compiler mal Mist bauen :)
 
In Fortran lassen sich Schleifen sehr einfach parallelisieren (do concurrent). Wenn man dann von Fortran aus auf C-Pointer zugreift, kann der Compiler sicher auch mal daneben greifen.
 
Gut, die Analyse an sich bleibt die gleiche: Natürlich unterscheiden sich malloc() Implementierungen stark, die Semantik ist aber die gleiche, und das unschöne ist, dass mit manchen Implementierungen ein Bug auch länger mal unentdeckt bleiben kann. Jetzt steht einfach nur die These im Raum, dass der Compiler diesen Bug verursacht haben könnte :)
 
Zurück
Oben