una sirve sabiendo que puede proyectos proceso para memoria mapa lenguaje hechos hacer desarrolladas con como aplicaciones aplicacion c frameworks module

sirve - Crear un sistema de módulos(carga dinámica) en C



que se puede hacer con c++ (9)

Hay un enfoque de bricolaje Si bien el método (y la posibilidad) de hacer esto varía de un sistema a otro, la idea general es abrir un archivo, leer el contenido del archivo en la memoria, hacer que dicha memoria sea ejecutable, inicializar un puntero a una posición válida dentro de esta memoria y ahí estás.

Por supuesto, esto es asumiendo que es solo código ejecutable, bastante improbable. El código probablemente también requiera que los datos se carguen en la memoria RAM, y puede requerir espacio para variables globales / estáticas. Puede cargarlo todo usted mismo, pero necesitará ir al código ejecutable y ajustar todas las referencias de memoria en él.

La mayoría de los sistemas operativos permiten la vinculación dinámica, que hace todo esto por ti.

¿Cómo se podría cargar el código compilado C en tiempo de ejecución y luego llamar funciones dentro de él? No es como llamar simplemente a exec ().

EDITAR: El programa que carga el módulo está en C.


Los lenguajes dinámicos como Perl hacen esto todo el tiempo. El intérprete de Perl está escrito en C, y muchos módulos de Perl están parcialmente escritos en C. Cuando se requieren esos módulos, los componentes C compilados se cargan dinámicamente sobre la marcha. Como se señala en otra respuesta, el mecanismo para almacenar esos módulos es DLL en Windows y bibliotecas compartidas (archivos .so) en UNIX. Creo que la solicitud para cargar una biblioteca compartida en UNIX es dlopen (). Probablemente pueda encontrar indicadores sobre cómo lograr esto en UNIX comenzando con la documentación de esa llamada. Para Windows, necesitaría buscar DLL y aprender a cargarlos dinámicamente en tiempo de ejecución. [O posiblemente pase por la capa de emulación Cygwin UNIX, lo que probablemente le permita usar las mismas llamadas en Windows que haría en UNIX, pero no lo recomendaría a menos que ya esté usando y compilando contra Cygwin.]

Tenga en cuenta que esto es diferente de simplemente vincular con una biblioteca compartida. Si sabe de antemano exactamente a qué código llamará, puede compilar contra una biblioteca compartida y la compilación estará "vinculada dinámicamente" a esa biblioteca; sin su manejo especial, las rutinas de la biblioteca se cargarán en la memoria solo cuando su programa realmente las llame. Pero no puede hacer eso si planea escribir algo capaz de cargar cualquier código de objeto arbitrario , código que no puede identificar ahora, en tiempo de compilación, pero que en cambio espera ser seleccionado de alguna manera en tiempo de ejecución. Para eso, tendrás que usar dlopen () y sus primos de Windows.

Puede ver la forma en que Perl u otros lenguajes dinámicos hacen esto para ver algunos ejemplos reales. La biblioteca de Perl responsable de este tipo de carga dinámica es DynaLoader; tiene tanto un componente Perl como un componente C, creo. Estoy seguro de que otros lenguajes dinámicos como Python tienen algo similar que podrías mirar; y Parrot, la máquina virtual para el Perl 6 inédito, seguramente tiene un mecanismo para hacer esto también (o lo hará en el futuro).

Para el caso, Java logra esto a través de su interfaz JNI (Java Native Interface), por lo que probablemente pueda ver el código fuente de OpenJDK para ver cómo lo logra Java tanto en UNIX como en Windows.


En Linux / UNIX puede usar las dlopen POSIX dlopen / dlsym / dlerror / dlclose para abrir dinámicamente las bibliotecas compartidas y acceder a los símbolos (incluidas las funciones) que proporcionan, consulte la página de manual para obtener más información.


También puedes mirar cpluff . Es una biblioteca de administración de complementos en c pura.


En Windows, así es como lo hago:

  • Generar código (en C porque es fácil encontrar compiladores y los requisitos de la biblioteca son mínimos)
  • engendrar un trabajo para compilar / vincularlo a un archivo DLL
  • cargarlo con LoadLibrary
  • obtener punteros de función con GetProcAddress

Los pasos generar / compilar / vincular generalmente toman menos de un segundo.


Ver que se ha respondido esta pregunta, pero que otros interesados ​​en este tema pueden apreciar un ejemplo de plataforma cruzada de una aplicación antigua basada en un complemento. El ejemplo funciona en win32 o linux, y establece y llama a una función llamada ''constructor'' en el archivo .so o .dll cargado dinámicamente especificado en el argumento del archivo. El ejemplo está en c ++ pero los procedimientos deberían ser los mismos para c.

//firstly the includes #if !defined WIN32 #include <dlfcn.h> #include <sys/types.h> #else #include <windows.h> #endif //define the plugin''s constructor function type named PConst typedef tcnplugin* (*PConst)(tcnplugin*,tcnplugin*,HANDLE); //loads a single specified tcnplugin,allmychildren[0] = null plugin int tcnplugin::loadplugin(char *file) { tcnplugin *hpi; #if defined WIN32 //Load library windows style HINSTANCE hplugin=LoadLibrary(file); if (hplugin != NULL) { PConst pinconstruct = (PConst)GetProcAddress(hplugin,"construct"); #else //Load it nix style void * hplugin=dlopen(file,RTLD_NOW); if (hplugin != NULL) { PConst pinconstruct = (PConst)dlsym(hplugin,"construct"); #endif if (pinconstruct != NULL) { //Try to call constructor function in dynamically loaded file, which returns a pointer to an instance of the plugin''s class hpi = pinconstruct(this, this, hstdout); } else { piprintf("Cannot find constructor export in plugin!/n"); return 0; } } else { piprintf("Cannot open plugin!/n"); #if !defined WIN32 perror(dlerror()); #endif return 0; } return addchild(hpi); //add pointer to plugin''s class to our list of plugins }

También se puede mencionar que si el módulo cuyas funciones desea llamar está escrito en c ++, debe declarar la función con extern "C" como:

extern "C" pcparport * construct(tcnplugin *tcnptr,tcnplugin *parent) { return new pcparport(tcnptr,parent,"PCPARPORT",0,1); }


Si está dispuesto a considerar el marco, Qt proporciona QPluginLoader: Qt 5 documentos (o para los antiguos documentos de Qt 4.8 ver aquí )

Si necesita / desea un control de grano más fino, Qt también proporciona un medio para cargar bibliotecas sobre la marcha con QLibrary: Qt 5 documentos (o para documentos antiguos de Qt 4.8 ver aquí )

Aún mejor, estos son portátiles en todas las plataformas.


dlopen es el camino a seguir. Aquí están algunos ejemplos:

Cargando un complemento con dlopen:

#include <dlfcn.h> ... int main (const int argc, const char *argv[]) { char *plugin_name; char file_name[80]; void *plugin; ... plugin = dlopen(file_name, RTLD_NOW); if (!plugin) { fatal("Cannot load %s: %s", plugin_name, dlerror ()); }

Compilando lo anterior:

% cc -ldl -o program program.o

Luego, asumiendo esta API para los complementos:

/* The functions we will find in the plugin */ typedef void (*init_f) (); init_f init; typedef int (*query_f) (); query_f query;

Encontrar la dirección de init () en el complemento:

init = dlsym(plugin, "init"); result = dlerror(); if (result) { fatal("Cannot find init in %s: %s", plugin_name, result); } init();

Con la otra función, query (), que devuelve un valor:

query = dlsym (plugin, "query"); result = dlerror(); if (result) { fatal("Cannot find query in %s: %s", plugin_name, result); } printf("Result of plugin %s is %d/n", plugin_name, query ());

Puede recuperar el ejemplo completo en línea .


para usuarios de GNU / Linux

La biblioteca de carga dinámica es un mecanismo que, con la ayuda de eso, podemos ejecutar nuestro programa y, en tiempo de ejecución, decidir qué función queremos usar. Creo que en algunos casos static variable static es posible.

Primero, comience viendo a man 3 dlopen o vea en línea

El archivo de encabezado que se requiere es: dlfcn y, como esto no forma parte del estándar, debería tenerlo como su archivo de objeto con esta biblioteca: libdl.(so/a) y, por lo tanto, necesita algo como:

gcc yours.c -ldl

entonces tiene un nombre de archivo a.out y puede ejecutarlo PERO no funciona correctamente y le explicaré por qué.

Un ejemplo completo:

primeros archivos de func1.c 2 func1.c y func2.c respectivamente. Queremos llamar a estas funciones en tiempo de ejecución.

func.c

int func1(){ return 1; }

func2.c

const char* func2(){ return "upgrading to version 2"; }

Ahora tenemos 2 funciones, hagamos nuestros módulos:

ALP ❱ gcc -c -fPIC func1.c ALP ❱ gcc -c -fPIC func2.c ALP ❱ gcc -o libfunc.so -shared -fPIC func1.o func2.o

para preguntar acerca de -fPIC => PIC

Ahora tiene dynamic library nombres de una dynamic library : libfunc.so

temp.c programa principal (= temp.c ) que quiere usar esas funciones.

archivos de encabezado

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

y el programa principal

int main() { // pointer function to func1 and func2 int ( *f1ptr )(); const char* ( *f2ptr )(); // for pointing to the library void* handle = NULL; // for saving the error messages const char* error_message = NULL; // on error dlopen returns NULL handle = dlopen( "libfunc.so", RTLD_LAZY ); // check for error, if it is NULL if( !handle ) { fprintf( stderr, "dlopen() %s/n", dlerror() ); exit( 1 ); } /* according to the header file: When any of the above functions fails, call this function to return a string describing the error. Each call resets the error string so that a following call returns null. extern char *dlerror (void) __THROW; */ // So, reset the error string, of course we no need to do it just for sure dlerror(); // point to func1 f1ptr = (int (*)()) dlsym( handle, "func1" ); // store the error message to error_message // because it is reseted if we use it directly error_message = dlerror(); if( error_message ) // it means if it is not null { fprintf( stderr, "dlsym() for func1 %s/n", error_message ); dlclose( handle ); exit( 1 ); } // point the func2 f2ptr = (const char* (*)()) dlsym( handle, "func2" ); // store the error message to error_message // because it is reseted if we use it directly error_message = dlerror(); if( error_message ) // it means if it is not null { fprintf( stderr, "dlsym() for func2 %s/n", error_message ); dlclose( handle ); exit( 1 ); } printf( "func1: %d/n", ( *f1ptr )() ); printf( "func2: %s/n", ( *f2ptr )() ); // unload the library dlclose( handle ); // the main return value return 0; }

Ahora solo tenemos que compilar este código (= temp.c ), por lo tanto, intente:

ALP ❱ gcc temp.c -ldl ALP ❱ ./a.out libfunc.so: cannot open shared object file: No such file or directory

¡No funciona! POR QUÉ fácil; porque nuestro programa a.out no sabe dónde encontrar la biblioteca relacionada: libfunc.so y por lo tanto nos dice que cannot not open ...

¿Cómo decirle al programa (= a.out ) para encontrar su biblioteca?

  1. usando ld linker
  2. utilizando la variable de entorno LD_LIBRARY_PATH
  3. usando la ruta estándar

primera manera, con ayuda de ld

usa -Wl,-rpath, y pwd y pon la ruta como argumento para ello

ALP ❱ gcc temp.c -ldl ALP ❱ ./a.out libfunc.so: cannot open shared object file: No such file or directory ALP ❱ pwd /home/shu/codeblock/ALP ALP ❱ gcc temp.c -ldl -Wl,-rpath,/home/shu/codeblock/ALP ALP ❱ ./a.out func1: 1 func2: upgrading to version 2

segunda manera

ALP ❱ gcc temp.c -ldl ALP ❱ ./a.out libfunc.so: cannot open shared object file: No such file or direc ALP ❱ export LD_LIBRARY_PATH=$PWD ALP ❱ echo $LD_LIBRARY_PATH /home/shu/codeblock/ALP ALP ❱ ./a.out func1: 1 func2: upgrading to version 2 ALP ❱ export LD_LIBRARY_PATH= ALP ❱ ./a.out libfunc.so: cannot open shared object file: No such file or

y tercera forma

tiene libfunc.so en su ruta actual, por lo tanto puede copiarlo en una ruta estándar para bibliotecas.

ALP $ sudo cp libfunc.so /usr/lib ALP ❱ gcc temp.c -ldl ALP ❱ ./a.out func1: 1 func2: upgrading to version 2

puede eliminarlo de /usr/lib y usarlo. Es tu decision.

NOTA

¿Cómo saber que nuestro a.out sabe acerca de su camino?
fácil:

ALP ❱ gcc temp.c -ldl -Wl,-rpath,/home/shu/codeblock/ALP ALP ❱ strings a.out | grep // /lib/ld-linux.so.2 /home/shu/codeblock/ALP

cómo podemos usarlo en c ++ ?
Mientras sepa que no puede porque g++ altera los nombres de las funciones mientras que gcc no lo hace, debería usar: extern "C" int func1(); por ejemplo.

Para más detalles, consulte las páginas de manual y los libros de programación de Linux.