Hi,
On Saturday 18 April 2009, Bernhard Schiffner wrote:
innerhalb eines "großen" Programms möchte ich zu Laufzeit nur einen kleinen Bestandteil ändern.
Ich will nun nicht das ganze Ding neu kompilieren und starten müssen (Maschinensteuerung!), sondern nur zu einem geeigneten Augenblick diese Änderung einbringen können.
Sind die Änderungen wirklich so begrenzt? Sprich: kannst Du es als Plugin bezeichnen?
Hat da irgendwer Erfahrung? Was entspricht bei Debian libdl.so? (Irgendwie in libc enthalten?)
Es gibt libdl.so in /lib (Paket: libc6) und in /usr/lib (Paket: libc6-dev). (Debian Lenny)
Benutzt Du ANSI C oder C++? Bei ersterem musst Du sehr vorsichtig vorgehen, bei letzterem kann man das C++-Typsystem ausnutzen, um es sicher zu gestalten.
C++: Du brauchst ein Interface, z.B.: extern "C" { MyPluginClass* initPlugin(); bool canUnloadPlugin(); }
Der eigentliche Plugin-Code ist dann in einer von MyPluginClass abgeleiteten Klasse, deren Instanz von initPlugin zurückgeliefert wird. Ausserdem sollte diese Klasse einen Instanzen-Counter implementieren, so dass canUnloadPlugin dir sagen kann ob Du das Plugin entladen darfst.
Laden musst Du natürlich mit RTLD_LOCAL - es sei denn Deine Interface-Funktionen enthalten einen eindeutigen Teil (Tcl hängt z.B. immer den Bibliotheksnamen dran - ein "require Tk" lädt libtk.so und initialisiert mit TkInit()). Die Funktionspointer bekommst Du via dlsym - daher auch extern "C", sonst musst Du das Name-Mangling vom C++-Compiler kennen.
Falls Du Qt verwendest schau Dir stattdessen mal QPluginLoader genauer an.
ANSI-C: das Interface wird entsprechend größer und Du musst Dir Gedanken machen, wie Du absicherst dass es keine Abhängigkeiten gibt während Du entlädst (dlclose).
Ein paar Tips zum Thema Plugin mit ANSI-C:
Handles als void* herumreichen - das Handle darf damit beliebig komplex werden, das Plugin weiß worum es geht (cast auf interne Struktur), für die benutzenden Funktionen ist das Handle nur als Identifikator interessant (entsprechende Funktionen: newMyHandle, freeMyHandle, doSomethingMyHandle).
Komplexere Objekte, die bei jedem Plugin gleich strukturiert sind bekommen pseudo-Objektorientierte Strukturen
Nicht jede Funktion einzeln auflösen, sondern von der Interface Funktion eine Struktur mit Funktionspointern geben lassen: -- globale plugin.h -- typedef void* MyHandle; typedef int bool; struct MyStreamStruct { int (*read)(MyStreamStruct*self,int length,void*buffer); int (*write)(MyStreamStruct*self,int length,void*buffer); void (*closeAndFree)(MyStreamStruct*self); //internal: MyHandle _internalhandle; }; struct MyPluginStruct { //Plugin-Maintenance: void (*initPlugin)(); bool canUnload(); //Handle-orientiertes Interface: MyHandle (*newHandle)(); void (*freeHandle)(MyHandle); void (*dosomethingHandle)(MyHandle); //Objektorientiertes Interface: struct MyStreamStruct* (*newStream)(const char*filename); }; -- Plugin-locale interface.h -- #include "global/plugin.h" #ifdef __cplusplus extern "C" #endif MyPluginStruct* initPluginMyExtensionX(); -- Plugin-lokale interface.c -- #include "interface.h" static void myInitFunction(){ /* ... */ } /* ... viel Code ...*/ static struct MyPluginStruct myiface = { myInitFunction, myCanUnloadFunction, myNewHandle, myFreeHandle, myDoSomething, NULL /*z.B. wenn das Plugin keine Streams implementiert*/ }; MyPluginStruct* initPluginMyExtensionX(){return &myiface;} ----
Konrad