On Friday 17 November 2000 19:57, Reinhard Foerster wrote:
On Fri, Nov 17, 2000 at 04:59:01PM +0100, Konrad Rosenbaum wrote:
Als s.object bezeichnet man das Teil, wenn man es gerade kompiliert oder _nur_ zur Laufzeit laedt.
Als s.lib bezeichnet man es, wenn es vom ELF-Loader im Kernel gerade geladen wird.
Falsche Defi. Gegenbeispiel: Bei a.out-Linux gibt es auch shared libs und diese werden nicht von einem "ELF-Loader" in den Kernel geladen. q.e.q.
[das heisst q.e.d. - oder?]
Wer hat was von "in den Kernel" gesagt? Der ELF Loader sitzt nur im Kernel, legt die Daten aber in den Userspace.
Um ganz genau zu sein laedt der ELF-Loader nur ldlinux.so und die laedt dann den Rest vom Userspace aus.
Was auch nicht ganz korrekt ist, denn vorher wird die gesamte Page-Tabelle des Prozesses aufgeloest und mit den Daten aus ldlinux.so neu aufgebaut. Ausserdem werden alle Dateideskriptoren mit close-on-exec-Flag geschlossen (ausser std{in,out,err} meistens alle).
Der Begriff "shared lib" sagt UEBERHAUPT NICHTS über das Wann und Wie des Linkens und Ladens aus. Shared sagt einfach nur, dass die Lib bei Nutzung durch mehrere Anwendungen nur einmal im Speicher rumlungert.
Irgendwie versuchen wir hier mit unterschiedlichen Worten das selbe zu sagen...
Ganz am Rande: dieser Effekt tritt auf, weil die Lib per mmap geladen und dann schreibgeschuetzt wird (zumindest der ausfuehrbare Teil, der Rest lebt per MAPPRIVATE im copy-on-write Status).
Du kannst also auch eine System mit shared libs bauen, die in jedem Binary (also mehrmals) vorhanden sind und trotzdem shared sind.
(Deswegen _shared_ lib oder? ;-) )
Haeh? Erklaer mal anhand von Dateien und Funktionen was Du meinst.
Soweit ich das System kenne gilt: habe ich zwei identische so's (auf unterschiedlichen Inode, also mit eigenem Speicher auf der Platte), nennen wir sie A.so und B.so und ich lade in Programm X die A.so und in Y die B.so, dann koennen sie auch noch so gleich sein und ich habe doch zweimal Speicher verbraucht. Lade ich sowohl von X, als auch von Y die A.so, so habe ich fuer den schreibgeschuetzten Bereich(*1) nur einmal Speicher verbraucht und der schreibbare Bereich(*2) verdoppelt sich, sobald einer der Prozesse schreibend darauf zugreift.
(*1)Funktionen und Konstanten (*2)Variablen und Link-Pointer in der Symboltabelle (auch Variablen, aber versteckte)
!!Und das gilt sowohl, wenn Kernel+ldlinux.so die Arbeit machen, als auch, wenn ich libdl benutze. Ich sehe jetzt also wirklich keinen Unterschied ausser dem Zeitpunkt(*).
(*)Nur fuer Experten: mit libdl hat man etwas mehr Kontrolle ueber die Sichtbarkeit der Symbole, aber das tut hier nix zur Sache.
Natuerlich ist das so unpraktisch, dass das kein Mensch macht. Zur Klaerung der Begriffe ist die Betrachtung aber sinnvoll.
Erklaer mal.
Was du zu erklaeren versuchst ist eine "dynamisch ladbare Bibliothek" Das diese meist gleichzeitig auch shared lib ist, liegt aus praktischen Gruenden auf der Hand, ist aber nicht zwingend.
Wie jetzt? DLL (Du nennst es deutsch "dynamisch ladbare Bibliothek") und shared lib und shared object sind das selbe, die Begriffe sagen das selbe aus, sie haben den selben Zweck, die Dateien sehen gleich aus (beide koennen a.out oder WinDLL oder ELF oder xyz_et_cetera sein). Kurz: sieht aus wie Rollmops, schmeckt wie Rollmops, ist Rollmops. ;-)
Ich verstehe wirklich nicht, worauf Du hinaus willst.
Also nur Haarspalterei, da es sich um die selbe Datei handelt.
Naja, so langsam wurde in diesem Thread alles durcheinanderegehauen.
Bring mal Ordnung rein!
mit .so Dateien habe ich zwei Moeglichkeiten: a) sie per Headerdatei einbinden und den Linker einen Eintrag in mein Program machen lassen, der von Kern als "ladt das Ding fuer mich, weil ich zu faul dazu bin" interpretiert wird. b) ich oeffne sie per dlopen(3) und lasse mir (Funktions-)Pointer auf die Symbole darin geben und rufe die entsprechenden Pointer auf
Der Witz bei Variante b) ist mMn, dass man eigentlich nur den Namen einer Funktion kennen muss (sowas wie ListFunctionNames()) und damit dann Funktionen laden kann, deren Namen zur Compilezeit des Hauptprogramms noch gar nicht bekannt waren. Das geht mit dem Mechanismus in a) nicht.
(ich habe Mechanismus b) noch nicht benutzt. Stimmt das, was ich hier gesagt habe? Stefan G.???)
Im Grunde ist es richtig, aber Du solltest auch wissen, welche Parameter Du brauchst, sonst bekommst Du einen netten SIGSEGV (auch unter dem Pseudonym "signal 11" bekannt). Es funktioniert etwa so:
--------------------- #include <dlfcn.h>
... void *handle=dlopen("libNixWeiter.so",RTLD_GLOBAL|RTLD_NOW);
if(handle==NULL)/*is schiefgegangen, keine libNixWeiter vorhanden*/ exit(1);
void (*somefunc)()=dlsym(handle,"soEineFunktion");
if(somefunc!=NULL) /*rufen wir das Teil mal auf:*/ somefunc();
/*aus der Spuk:*/ dlclose(handle); ----------------------
Mit Variablen geht es aehnlich, Du must immer nur den richtigen Pointertyp (oder Cast) anwenden.
Nachteil der Sache: man kann sich ganz leicht in den Pointern verheddern und stolpert ueber Segfaults. Ausserdem wird das Debugging anspruchsvoller (zumindest fuer den Debugger).
Und: beim compilieren musst Du gewaltig mit Flags um Dich schmeissen, weil der Code bestimmte zusaetzliche Bedingungen erfuellen muss, damit es funzt (-fPIC ist noch das harmloseste ;-) ).
Und: bei C++ hoert der Spass auf! Die Symbole werden echt grausam (Beispiel gcc 2.7.3, main: main_PPci). (Es gibt auch dafuer Workarounds...)
Konrad