2008-12-25 6 views
8

beim Laden eine gemeinsam genutzte Bibliothek wird über die Funktion geöffnet dlopen(), gibt es eine Möglichkeit für sie in wesentlichen Programmfunktionen zu nennen?Wie würde eine geladene Bibliotheksfunktion ein Symbol in der Hauptanwendung aufrufen?

+0

Die Antworten unten beantworten die Frage sehr gut, aber ich muss fragen - könnten Sie den weiteren Kontext dieser Anforderung erklären? Wenn ich die Notwendigkeit gefunden habe, dies zu tun, ist es entweder ein Erweiterbarkeits-/Plugin-Modell zu konstruieren oder weil mein Programm nicht sehr gut durchdacht ist. – reuben

+0

könnte es auch für Inversionskontrolle verwendet werden, nein? Definieren Sie den Fluss der Anwendung in der Bibliothek, während die eigentliche Implementierung in der Hauptanwendung ist – hhafez

+0

Denken Sie an ein Perl XS-Modul. Es muss Low-Level-Perl-Funktionen (sagen wir NewSViv(), um ein SV aus einer ganzen Zahl zu erstellen); Es ist praktisch, wenn das Modul die newSViv() -Funktion von Perl verwendet, statt seine eigene Kopie in das gemeinsame Objekt des Moduls einzubetten. Außerdem benötigt der Code die Standard-C-Bibliothek. –

Antwort

17

Code of dlo.c (die lib):

#include <stdio.h> 

// function is defined in main program 
void callb(void); 

void test(void) { 
    printf("here, in lib\n"); 
    callb(); 
} 

Compile mit

gcc -shared -olibdlo.so dlo.c 

Hier ist der Code des Hauptprogramms (von dlopen manpage kopiert und angepasst):

#include <stdio.h> 
#include <stdlib.h> 
#include <dlfcn.h> 

void callb(void) { 
    printf("here, i'm back\n"); 
} 

int 
main(int argc, char **argv) 
{ 
    void *handle; 
    void (*test)(void); 
    char *error; 

    handle = dlopen("libdlo.so", RTLD_LAZY); 
    if (!handle) { 
     fprintf(stderr, "%s\n", dlerror()); 
     exit(EXIT_FAILURE); 
    } 

    dlerror(); /* Clear any existing error */ 

    *(void **) (&test) = dlsym(handle, "test"); 

    if ((error = dlerror()) != NULL) { 
     fprintf(stderr, "%s\n", error); 
     exit(EXIT_FAILURE); 
    } 

    (*test)(); 
    dlclose(handle); 
    exit(EXIT_SUCCESS); 
} 

Bauen mit

gcc -ldl -rdynamic main.c 

Ausgang:

[[email protected] dlopen]$ LD_LIBRARY_PATH=. ./a.out 
here, in lib 
here, i'm back 
[[email protected] dlopen]$ 

Die -rdynamic Option, alle Symbole in der dynamischen Symboltabelle setzt (die in den Speicher abgebildet wird), nicht nur die Namen der verwendeten Symbole . Lesen Sie weiter darüber here. Natürlich können Sie auch Funktionszeiger (oder eine Struktur von Funktionszeigern) angeben, die die Schnittstelle zwischen der Bibliothek und Ihrem Hauptprogramm definieren. Es ist eigentlich die Methode, die ich wahrscheinlich wählen würde. Ich von anderen Leuten gehörte, dass es nicht so einfach ist, -rdynamic in Windows zu tun, und es wäre auch für eine saubere Kommunikation zwischen Bibliothek und Hauptprogramm zu machen (Sie haben auf präzise Kontrolle bekommen, was genannt und nicht sein), aber es erfordert auch mehr Hauswirtschaft.

+0

Awesome Antworten. Jemand hat 10k rep verdient :-) –

+0

Das ist eine interessante Art, das Casting zu machen. Ist es nicht normaler, den Rückgabewert von dlsym() in den Zeiger zur Funktion zu konvertieren, anstatt so zu tun, dass der Zeiger, den Sie zuweisen, derselbe Typ ist wie die Funktion dlsym() zurückgibt? –

+0

C sagt nicht, was passiert, wenn Sie von einem void * auf einen Funktionszeiger umwandeln. Leider habe ich den Absatz nicht gefunden, der besagt, dass es ein undefiniertes Verhalten ist, aber die Manpage sagt, dass es so ist. –

4

Ja, Wenn Sie Ihrer Bibliothek einen Zeiger auf diese Funktion liefern, bin ich sicher, dass die Bibliothek die Funktion im Hauptprogramm ausführen/ausführen kann.

Hier ist ein Beispiel, kompiliert haben es nicht so passen;)

/* in main app */ 

/* define your function */ 

int do_it(char arg1, char arg2); 

int do_it(char arg1, char arg2){ 
    /* do it! */ 
    return 1; 
} 

/* some where else in main app (init maybe?) provide the pointer */ 
LIB_set_do_it(&do_it); 
/** END MAIN CODE ***/ 

/* in LIBRARY */ 

int (*LIB_do_it_ptr)(char, char) = NULL; 

void LIB_set_do_it(int (*do_it_ptr)(char, char)){ 
    LIB_do_it_ptr = do_it_ptr; 
} 

int LIB_do_it(){ 
    char arg1, arg2; 

    /* do something to the args 
    ... 
    ... */ 

    return LIB_do_it_ptr(arg1, arg2); 
} 
+0

do_it_ptr nimmt einen Zeiger auf eine Funktion, die 3 Char-Argumente erwartet; Sie weisen Funktionszeiger für Funktionen zu, die nur 2 char-Argumente benötigen. Die externe Deklaration für doit() wird kaum benötigt. Der do_it_ptr wird nicht benötigt; Sie können do_it einfach mit dem Namen übergeben, an dem Sie derzeit do_it_ptr übergeben. Etc! –

+0

das ist richtig :) in der Tat könnten Sie auch die LIB_get_it() loswerden und nur eine neue LIB_do_it (int (* do_it_ptr) (char, char, char)) {Rückgabe do_it_ptr (arg1, arg2, arg3)} – hhafez

1

Die Funktion dlopen(), wie von @litb beschrieben, wird hauptsächlich auf Systemen bereitgestellt, die Objektdateien im ELF-Format verwenden. Es ist ziemlich mächtig und lässt Sie kontrollieren, ob Symbole, die von der geladenen Bibliothek referenziert werden, vom Hauptprogramm erfüllt werden können, und im Allgemeinen lassen Sie sie zufriedenstellen. Nicht alle Shared-Library-Ladesysteme sind so flexibel - beachten Sie, wenn es um die Portierung Ihres Codes geht.

Der Callback-Mechanismus, der von @hafez beschrieben wird, funktioniert jetzt, dass die Knicke in diesem Code geglättet werden.

+0

begradigte die Knicke;) Danke – hhafez