| Revision History | ||
|---|---|---|
| Revision 0.1 | 13 / 08 / 2004 | Revised by: Víctor Rodríguez López |
| Inicial. | ||
dlsym()g_module_symbol()Los plugins son librerías que pueden ser cargadas bajo demanda en tiempo de ejecución. Es una forma muy elegante para construir programas extensibles y modulares.
dlsym()En este ejemplo se crean dos librerías, una de ellas dinámica libb.so y la otra estática liba.a. El código de este ejemplo se puede encontrar en el repositorio publico del grupo ARCO.
Estas librerías serán llamadas más tarde en el programa principal, libb.so será cargada como un plugin, por otro lado liba.a será tratada como librería estática, y así de esta manera resaltar las diferencias que existen entre ambas.
Vamos a ver en detalle el Makefile principal:
CC=gcc CFLAGS=-Wall -ggdb -IdiraLDFLAGS=-Ldira
LDLIBS=-la -ldl
all: libs main main: main.o # # Estas reglas ejecutan los makefiles que hay en los subdirectorios # libs: $(MAKE) -C dira $(MAKE) -C dirb clean: $(RM) *.o *~ main $(MAKE) -C dira clean $(MAKE) -C dirb clean
En dira y dirb se encuentra el código de las dos librerías de nuestro ejemplo.
No tiene nada especial, simplemente las funciones que queremos que implemente la librería.
Únicamente se resaltan los aspectos importantes del fichero main.c para la creación de plugins.
Indicamos el archivo de cabecera para la librería libdl.
#include <dlfcn.h>
dlopen carga en tiempo de ejecución la librería indicada en
el primer argumento, el segundo argumento es un flag de la librería.
pLibb = dlopen("./dirb/libb.so.1.0.0", RTLD_LAZY);
Ahora se obtiene la referencia al símbolo (o función) de la
librería, para ello se usa dlsym.
pFuncion = dlsym(pLibb, "b"));
Usar el símbolo:
pFuncion(i);
Liberamos recursos:
dlclose(pLibb);
El código de este ejemplo se puede encontrar en carga_implicita.
En las opciones de complilación, se añaden las
opciones: -fPIC compilación independiente
de la posición, y -rdynamic
equivale a la opcion -Wl,--export-dynamic
que evita tener que cargar los símbolos de las librerías.
CFLAGS=-Wall -ggdb -Idira -fPIC LDFLAGS=-Ldira -rdynamic
Cuando la librería es llamada por primera vez deberá ser cargada y registrada en la lísta, por eso se incluye en las librerías el siguiente código.
static void init() __attribute__((constructor));
static void init() {
register_plugin("b", &b);
}
Creamos una lista enlazada donde se irán almacenando los plugins que vayamos usando. El código en main.c
struct plugin {
char* key;
void (*f)(int);
struct plugin* next;
};
static struct plugin* protos;
Habrá tres funciones para tratar la lísta enlazada:
register_plugin,
unregister_plugin y
busca. Los nombres de las funciones
son descriptivos de su funcionamiento.
void
register_plugin (char* key, void (*f)(int))
{
struct plugin* p = (struct plugin*) malloc(sizeof(struct plugin));
p->next = protos;
p->f = f;
p->key = key;
protos = p;
printf("** Plugin '%s' registrado con éxito.\n", key);
}
void
unregister_plugin (char* key)
{
struct plugin *prev = NULL, *p = protos;
while (p) {
if (0==strcmp(p->key, key)) break;
prev = p;
p = p->next;
}
if (p) {
if (prev) prev->next = p->next;
else protos = p->next;
free(p);
}
}
static struct plugin*
busca(char* key)
{
struct plugin *p = protos;
while (p) {
if (0==strcmp(p->key, key)) break;
p = p->next;
}
return p;
} La función f() es la que realmente
hace la llamada al símbolo (o función) del plugin.
void
f (char* key, int i)
{
struct plugin* p;
p = busca(key);
if (!p) {
char libname[PATH_MAX];
char path[PATH_MAX];
sprintf(libname, "%s/dir%s/lib%s.so", getcwd(path, PATH_MAX), key, key);
printf("Intentando cargar '%s'.\n", libname);
dlopen(libname, RTLD_LAZY);
p = busca(key);
}
if (p) p->f(i);
else
fprintf(stderr, "ERROR: El plugin '%s' no está disponible.\n", key);
}
Llamada a la función del plugin:
f("b",i);
g_module_symbol()El código de este ejemplo se puede encontrar en carga_explicita.
Los únicos cambios en el Makefile son los relacionados con la librería glib. Dentro de glib podemos encontrar gmodule que sirve para hacer carga dinámica 'plug-ins'.
CFLAGS=-Wall -ggdb -Idira `pkg-config --cflags glib-2.0` `pkg-config --cflags gmodule-2.0` LDLIBS=-la -ldl `pkg-config --libs gmodule-2.0` `pkg-config --libs glib-2.0`
A destacar g_module_open para cargar la
librería, y g_module_symbol para
obtener la referencia al simbolo del plugin. El código del
programa principal en main.c
pmodule = g_module_open("./dirb/libb.so.1.0.0",G_MODULE_BIND_LAZY);
...
...
...
g_module_symbol(pmodule,"b", (gpointer*)&pFuncion);
Liberar recursos:
g_module_close(pmodule);
El código de este ejemplo se puede encontrar en carga_implicita.
La diferencia en el Makefile sólo tiene que ver con glib, por tanto los cambios son los mismos del ejemplo anterior.
El código en main.c
Cabecera para el manejo de tablas Hash
#include <glib/ghash.h>
Creación de una tabla para almacenar los plugins, y funciones para registra o des-registrar plugins usando la tabla Hash.
static GHashTable* plugin = NULL;
void
register_plugin (char* key, void (*f)(int))
{
if (plugin == NULL)
plugin = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
g_hash_table_insert(plugin,key,f);
printf("** Plugin '%s' registrado con éxito.\n", key);
}
void
unregister_plugin (char* key)
{
if (plugin != NULL)
g_hash_table_remove(plugin,key);
}
Esta función es la que llama al símbolo del plugin.
void
f (char* key, int i)
{
GModule* pmodule;
void (*p)(int);
if (plugin == NULL)
plugin = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
p = g_hash_table_lookup(plugin, key);
if (!p) {
char libname[PATH_MAX];
char path[PATH_MAX];
sprintf(libname, "%s/dir%s/lib%s.so", getcwd(path, PATH_MAX), key, key);
printf("Intentando cargar '%s'.\n", libname);
if((pmodule = g_module_open(libname,G_MODULE_BIND_LAZY))==NULL){
g_warning("No se pudo cargar la librería %s", libname);
exit(1);
}
p = g_hash_table_lookup(plugin, key);
}
if (p) p(i);
else
fprintf(stderr, "ERROR: El plugin '%s' no está disponible.\n", key);
}
[2] Plugins, Repositorio publico del grupo ARCO, 2004.
[3] Librerías, mini-doc del grupo ARCO, 2003.