Gemeinsame C++-Laufzeitbibliothek unter Linux?
-
Danke!
Nur warum löst es dann Windows so umständlich?
ChrisM
-
Unter Windows ist es genau das selbe1
-
Nein, unter Windows wird die Standardbibliothek statisch gelinkt und wenn ich in einer EXE Speicher allokiere, kann ich den in einer DLL nicht freigeben und andersrum!
Sonst krieg ich eine Debug Assertion Failed und der Speicher wird nicht freigeben!ChrisM
-
Original erstellt von ChrisM:
**Nein, unter Windows wird die Standardbibliothek statisch gelinkt und wenn ich in einer EXE Speicher allokiere, kann ich den in einer DLL nicht freigeben und andersrum!
Sonst krieg ich eine Debug Assertion Failed und der Speicher wird nicht freigeben!ChrisM**
Sicher, dass du da jetzt nicht mehrere Themen wild durcheinander würfelst? Wie lange programmierst du Win32? Wie lange unter Linux bzw. Unix?
Bitte belege deine Aussage mit einem kurzen, aber konkreten Beispiel an Hand dessen man nachvollziehen kann, *was* du wirklich meinst. Vielleicht drückst du dich nur etwas ungenau aus.
-
Also unter Windows programmier ich schon länger, aber unter Linux hab ich noch gar nicht programmiert. Mir gehts ja nur darum, dass das was ich jetzt programmiere, später auch bequem portierbar ist.
Beispiel:
// Diese Funktion ist in der DLL/.so!! int* ExportierteFunktion(void) { return new int; } // Die EXE: int main() { int* pTest = ExportierteFunktion(); // ExportierteFunktion() muss man natürlich vorher irgendwie laden, ich glaub unter Linux mit dlopen() oder so delete pTest; // HIER!!!!!!!! return 0; }
Die mit "hier" markierte Zeile geht z.B. unter Windows nur, wenn ich die shared Version der Laufzeitbibliothek verwende, sonst erhalte ich nur eine Debug Assertion, denn: Das new int, das in der DLL ausgeführt wird, wird in eine Liste in der Laufzeitbibliothek eingetragen (da steht dann drin, 4 Byte großer Speicherblock bei 0xirgendwas allokiert). Wenn ich jetzt bei "hier" den Speicherbereich wieder freigeben will, geht das nur, wenn die Liste die selbe ist, sonst findet das delete der Bibliothek die Speicherallokierung nicht in der Liste!
Und mit der Standardeinstellung wird die Standardbibliothek halt immer zu jeder EXE/DLL einzeln statisch gelinkt und man hat getrennte Liste (=Fehler).Deswegen würd ich jetzt wissen, ob sowas unter Linux geht oder noch einfacher:
Ist obiger Code unter Linux lauffähig?ChrisM
-
Gut, einfache Frage, würde sowas unter Linux funktioniert:
void Test(int *p) { delete p; // Speicher freigeben, der von anderem Modul allokiert wurde! }
Irgendeine Executable, die die .so benutzt:
// ... ::Test(new int); // Unter Windows gäbe das jetzt eine Debug Assertion, wenn ich nicht die gemeinsame Laufzeitbibliothek verwende! // ...
ChrisM
-
die Frage haben glaub ich alle mittlerweile verstanden.
Meine Antwort (zum 3. mal): probier es einfach mal aus, wenn es jemand wüsste, dann hätte er schon längst geantwortet
-
Hab aber grad gar kein Linux drauf und noch nie was unter Linux kompiliert.
ChrisM
-
Also, zu Deinem Beispiel, bei dem es unter Windows "kracht", wenn Du nicht diese ominöse "Shared-Version" der DLL verwendest:
Zunächst erst noch einmal folgendes (und glaub es uns!): EGAL ob Du unter Windows oder unter Linux eine Bibliothek statisch oder dynamisch linkst, läuft sie in DEINEM Prozess. Eine Lib. ist nur ein Stück Programmcode, stellt aber keinen neuen Prozess dar (es sei denn, innerhalb der DLL/.so wird ein Thread oder Prozess erstellt).
Dann gibt es nun mehrere Möglichkeiten die Bibliothek einzubinden, entweder der Code der Lib. wird fest in den Programmcode rein gelinkt, was die ausführbare Datei grösser macht, oder man benötigt eine separate .dll/.so . Dabei kann man nun auch irgend wie die DLL so an das Programm binden, dass man die Funktionen direkt aufrufen kann (dabei wird das Vorhandensein der DLL in jedem Fall gefordert) oder man läd die DLL/.so per Hand (unter Linux mit dlopen, unter Windows gibts das auch, der Name der Api-Funktion fällt mir aber gerade nicht ein), und lässt sich zu jeder Funktion über den Namen die Adresse zurück geben, an welcher Stelle die Funktion nun liegt um sie später aufzurufen (siehe "Zeiger auf Funktionen").
Bei allen Varianten kommt kein eigener Heap ins Spiel. Du kannst ja dein Beispiel , bei dem es kracht nochmal testen, indem Du statt "new" und "delete" mal "malloc" und "free" verwendest. Wahrscheinlich läuft es dann.
Aber sei Windows dankbar, dass es eine Fehlermeldung gibt. Denn solchen Code sieht man leider sehr häufig, obwohl er immer ein grosses Risiko darstellt. Warum? Folgendes Gedankenspiel: Man schreibt eine DLL, die eine Funktion besitzt um Speicher zu allokieren und diese Adresse gibt die Funktion auch zurück. Sagen wir mal, der Speicher sei mit "new char[length]" angelegt worden. Nun unser Programm: Es ruft diese Funktion auf, den Rückgabewert (also die Speicheradresse) haben wir in einem "char *ptr" abgespeichert und nun löschen wird den Speicher mit "delete [] ptr".
Was kann schief gehen? Da die Bibliothek und das Programm getrennt übersetzt wurden, kann das "new" der Bibliothek zu dem "delete" aus dem Programm evtl nicht kompatibel sein. Unter Linux sieht man solch einen Code sehr oft, aber da kommt i.d.R. auch immer der selbe Compiler zum Einsatz, so dass es Funktioniert. Aber es ist nicht sauber, evtl. kann es einen Unterschied machen, welchen Compiler man verwendet, oder vielleicht kann es schon beim selben Compiler, aber unterschiedlichen Flags zu relevanten Unterschieden kommen.
Beispiel: Übersetzen der Bibliothek:
"new char[length]" führt zu folgender Speicherallokation: size_of(char)==1, also werden length+2 Bytes reserviert, beginnend an der Adresse X. In Adresse X und X+1 steht nun ein 16Bit-Wert, mit dem numerischen Inhalt "length". "new char[length]" gibt aber als Adresse X+2 zurück. Entsprechend funktioniert hier ein "delete [] ptr", indem bei ptr-2 nach dem 16Bit-Wert für die Array-Grösse geschaut wird.
Beispiel: Übersetzen des Programmcodes:
Durch einen anderen Compiler oder sogar nur durch andere Flags sei nun folgendes denkbar. Ein "new char[length]" führt HIER zu einem reservierten Speicherbereicht der Länge length+4, wobei ein 32Bit-Wert für die Array-Grösse hinterlegt wird. Entsprechend schaut hier "delete [] ptr" bei ptr-4 nach der Array-Grösse.
Nun schau Dir nochmal Dein Beispielprogramm an, und überleg Dir, wie Du so etwas ausschliessen kannst. Die Lösung dazu lautet (analog zu "wenn malloc, dann free / wenn new, dann delete" & nicht mischen): Wenn Speicher in einer Bibliothek reserviert wird, dann soll er auch dort wieder Freigegeben werder, gleiches gilt für den eigentlichen Programmcode. Ich denke, wenn Du Dich daran hältst, hat sich Dein Problem eh erledigt (oder nun zwei Heaps oder nur ein Heap existiert).
-
Danke, das war eine Antwort, die mir wirklich was geholfen hat! Ich weiß, dass Bibliotheken immer in den gleichen Prozess geladen werden, aber wenn ich a.exe und a.dll habe und beide linken die Standardbibliothek statisch, dann hab ich halt auch zwei Standardbibliotheken, obwohl ich nur einen Prozess habe!
Das mit dem Freigeben im gleichen Prozess ist leicht gesagt, aber in der Praxis sehr schwer umzusetzen
Nur ein paar Beispiele, bei denen ich nicht weiß, wie ich das Freigeben im gleichen Modul machen soll:
- Beim Wurf einer Exception
- Beim Umgang mit Smart Pointern, denn diese können ja überall freigegeben werdenDeswegen muss ich wissen, ob das Beispiel oben krachen würde unter Linux. Unter Windows kann ich das ja verhindern, aber wenn ich es unter Linux nicht verhindern kann, wird das sehr, sehr viel Probleme bei einem eventuellen Portierten geben!
ChrisM
-
So, ist geklärt. Unter Linux gibts auch eine gemeinsame Laufzeitbibliothek und die heißt libc.so
ChrisM
-
Ich hatte damit noch nie Schwierigkeiten, dass Speicher, der in einer DLL angelegt wurde auch wieder von dieser freigegeben wird. Ok, Exceptions sind eh etwas besonderes, da gibt es auch noch mehr Probleme als nur den Speicher freizugeben, wenn eine Bibliothek eine Exception wirft. Deshalb werfen aber meine Bibliotheken auch keine Exceptions
Aber mal von Exceptions abgesehen... Statt in meinem Hauptprogramm "delete ptr_xyz;" zu schreiben, rufe ich einfach eine Funktion aus meiner Bibliohek auf, z.B. "Erase_XYZ(ptr_xyz);" und in dieser Funktion der Bibliothek steht das "delete parameter_xyz;", fertig!