Gemeinsame C++-Laufzeitbibliothek unter Linux?



  • Oder muss man die Standardbibliothek wirklich immer statisch zur Executable linken lassen?

    Nein!

    probier es doch einfach mal aus mit dem Freigeben des Speichers!



  • > Nun wollte ich fragen, ob es sowas auch unter Linux gibt, oder ob die
    > Laufzeitbibliothek immer statisch zum Programm gelinkt werden muss?

    Per default erstellt der GCC immer Programme, die dynamisch gegen Shared Libraries (die *.so Dateien) gelinkt sind. Solltest du tatsächlich einmal etwas statisch compilieren wollen/müssen, musst du dem Compiler dies mit der Option -static mitteilen und eventuell gegen statische Bibliotheken (Dateiendung *.a; das Gegenstück zu *.so Dateien) linken.

    > unter Windows gibt es in Visual C++ die Möglich, eine s.g. Multithreaded DLL
    > Laufzeitbibliothek zu verwenden, d.h. die Laufzeitbibliothek wird zur
    > Laufzeit aus msvcrt.dll geladen.

    Wenn es dir darum geht, dass du benötigte Bibliotheken nicht zur Compilezeit sondern erst später zur Laufzeit verwenden möchtest, dann solltest du dir mal die Manpage zu dlopen() anschauen:

    dlopen(3)
    http://www.freebsd.org/cgi/man.cgi?query=dlopen&apropos=0&sektion=0&manpath=SuSE+Linux%2Fi386+7.3&format=html (Manpage aus SuSE/Linux 7.3)
    http://www.freebsd.org/cgi/man.cgi?query=dlopen&apropos=0&sektion=0&manpath=FreeBSD+5.0-RELEASE&format=html (Manpage aus FreeBSD 5.0)

    > Das hat z.B. den Vorteil, dass [...] mehrere DLLs, die [...] in
    > einem Prozess laufen, auf den Heapspeicher der anderen DLLs zugreifen können
    > und vor allem auch freigeben können.

    Hört sich verdammt nach IPC und Shared Memory an.

    shmget(2)
    http://www.freebsd.org/cgi/man.cgi?query=shmget&apropos=0&sektion=0&manpath=SuSE+Linux%2Fi386+7.3&format=html (Manpage aus SuSE/Linux 7.3)
    http://www.freebsd.org/cgi/man.cgi?query=shmget&apropos=0&sektion=0&manpath=FreeBSD+5.0-RELEASE&format=html (Manpage aus FreeBSD 5.0)

    IPC:Shared Memory
    http://www.cs.cf.ac.uk/Dave/C/node27.html#SECTION002700000000000000000

    Programming in C
    UNIX System Calls and Subroutines using C,
    http://www.cs.cf.ac.uk/Dave/C/CE.html



  • Original erstellt von ChrisM:
    **Was ist ipc? Also bei Visual C++ kann man als Linktyp für die Standardbibliothek "Multithreaded DLL" einstellen, dann verwenden alle DLLs und die EXE eines Prozesses eine geteilte Version der Standardbibliothek und man kann daher Heapspeicher freigeben, egal in welchem anderen Modul er allokiert wurde.

    ChrisM**

    Eine Shared Library (eine *.dll ist ja erst einmal auch nix anderes) enthält lediglich eine Ansammlung von Funktionen, die du in deinem Programm verwenden kannst. Wenn eine dieser Funktionen Speicher allokiert, kann dein Programm natürlich auf diesen Speicher zugreifen -- sofern dir die Funktion einen Zeiger auf den allokierten Speicher zur Verfügung stellt.

    Eine Shared Library enthält kein ausführbares Programm (zumindest in der UNIX Welt nicht), das von sich aus Speicher allokiert/freigibt.

    Dein Programm kann natürlich aus mehrere Prozessen oder aus mehrere Threads bestehen, die wieder untereinander (über Pipes oder Shared Memory) kommunizieren. Eine Bibliotheksfunktion kann ebenfalls mit Prozessen (siehe Manpage zu fork(2))oder Threads (siehe Dokumentation zu POSIX Threads) arbeiten.

    Bradford Nichols, Dick Buttlar & Jacqueline Proulx Farrell
    Pthreads Programming - A POSIX Standard for Better Multiprocessing
    http://www.oreilly.de/catalog/pthread/

    Richard W. Stevens
    UNIX Network Programming, Volume 2, Second Edition: Interprocess Communications, Prentice Hall, 1999, ISBN 0-13-081081-9.
    http://www.kohala.com/start/unpv22e/unpv22e.html
    Um "den Stevens" wirst du in der UNIX Programmierung eh nicht drum rumkommen.

    Bill Gallmeister
    POSIX.4 - Programming for the Real World
    [url]http://www.oreilly.de/catalog/posix4/index.html[/ur]
    Insbesondere Kapitel 4 ("Chapter 4: Better Coordination: Messages, Shared Memory, and Synchronization Communicating Between Processes ")
    Auszug aus dem Inhalt:

    Chapter 4: Better Coordination: Messages, Shared Memory, and Synchronization Communicating Between Processes
    ..POSIX.1 Communication: Pipes and FIFOs
    ..System V Message Queues
    ..POSIX.4 Message Queues
    ..POSIX.4 Shared Memory and File Mapping
    ..Synchronizing Multiple Processes
    ..Conclusion
    ..Exercises



  • Danke für die ganzen Infos, aber irgendwie habt ihr das Problem noch nicht ganz verstanden- oder ich nicht. 😃

    Also unter Windows bei C++ wird ja jedes Programm (auch eine DLL) zur C++-Standardbibliothek gelinkt, die ja auch u.a. new und delete implementiert. In dieser Bibliothek gibt es jetzt eine Liste von allen allokierten Speicherbereichen. Wenn ich jetzt delete aufrufe, wird erst geprüft, ob ich den Speicher überhaupt allokiert habe.

    Wenn ich jetzt eine EXE habe, die eine DLL verwendet, haben beide die Standardbibliothek statisch gelinkt, d.h. sie haben getrennte "Heaplisten", was auch heißt, dass ich in der DLL keinen von der EXE allokierten Speicher freigeben kann und andersrum.

    Nun kann man das umgehen, indem man dem Visual C++ sagt, dass man gerne die Standardbibliothek dynamisch als DLL gelinkt haben möchte. Jetzt liegt die Standardbibliothek in einer DLL (msvrt6.dll oder so heißt die) und jeweils ein Prozess hat eine "Heapliste".

    Nun zu Linux: Kann ich unter Linux mit den Defaulteinstellungen in einer Executable Speicher freigeben, der in einer .so allokiert wurde und andersrum?

    ChrisM



  • speicher wird nie IN einer so allokiert. eine so hat also keinen speicher. dynamischer speicher wird im datensegment des prozesses verwaltet. entweder von new oder malloc. (oder anderen methoden aus anderen sprachen).

    dh du bekommst eine adresse zurück, die immer zum prozess gehört. über diese adresse wird dein speicher verwaltet. wenn du jetz ein adresse = malloc(10101) machst und du es dann wieder mit free(adresse) frei gibst, ist das egal in welcher lib das passiert oder wo auch immer, so lange es im gleichen programm passiert. dh du brauchst das nur wieder mit free freigeben.

    wenn du eine lib haben willst, die resistent speicher allokieren kann, der der lib immer zur verfügung steht, dann musst du shm verwenden, weil der mit malloc allokierte speicher immer beim beenden des programm automatisch verschwindet.
    malloc ist keine routine des betriebssystems sondern der stdlib. (für new gilt das gleiche). wenn ein prozess beendet wird, dann wird auch das datensegmente verschwinden. dh du weißt nicht mehr wo dein speicher war bzw vielleicht wird der schon wieder von einem anderen programm verwendet.

    die glibc hat sehr gute und einfach verständliche doku darüber. www.gnu.org. da steht alles genau drin.

    hoffe, das hilft.



  • zusatz:
    immer bei einem system bleiben.
    mallocierter speicher free-en und newen speicher deleten 🙂 nie mischen.



  • 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:

    einelib.so:

    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 werden

    Deswegen 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!


Anmelden zum Antworten