Плагины
Наиболее простой способ обеспечения расширяемости ваших программ -
использование модулей , известных как плагины .
К числу известных программ такого типа относятся веб-броузеры,плееры :
в веб-броузерах например плагины используются для поддержки java,flash.
В данной статье модуль и плагин суть одно и то же .
1. Реализация плагина
Есть 4 функции для работы с плагинами.
Они являются частью библиотеки dl (Dynamic Loader).
- dlopen
- Загрузка модуля в память.
- dlclose
- Выгрузка модуля из памяти.
- dlsym
- Возвращает функцию внутри модуля
- dlerror
- Обработка ошибок
2. Пример
Пример реализован как загрузчик , который берет имя плагина как командный аргумент .
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <dlfcn.h>
#define PATH_LENGTH 256
int main(int argc, char * argv[])
{
char path[PATH_LENGTH], * msg = NULL;
int (*my_entry)();
void * module;
/* build the pathname for the module */
getcwd(path, PATH_LENGTH);
strcat(path, "/");
strcat(path, argv[1]);
/* load the module, and resolve symbols now */
module = dlopen(path, RTLD_NOW);
if(!module) {
msg = dlerror();
if(msg != NULL) {
dlclose(module);
exit(1);
}
}
/* retrieve address of entry point */
my_entry = dlsym(module, "entry");
msg = dlerror();
if(msg != NULL) {
perror(msg);
dlclose(module);
exit(1);
}
/* call module entry point */
my_entry();
/* close module */
if(dlclose(module)) {
perror("error");
exit(1);
}
return 0;
}
|
После загрузки плагина загрузчик ищет в символьной таблице с помощью команды dlsym
адрес функции entry.
На этот адрес назначается указатель .
Затем плагин выгружается .
int (*my_entry)()
Компиляция главной программы :
$ gcc -o loader main.c –ldl
|
3. Два простых плагина
Нет каких-то специальных прототипов для обьявления в плагине функции entry.
Естественно , название entry тоже может быть произвольным.
В плагине может быть несколько функций.
Ниже приведены примеры 2-х модулей:
int entry()
{
printf("I am module one!\n");
return 0;
}
|
int entry()
{
printf("I am module two!\n");
return 0;
}
|
Компиляция плагинов:
$ gcc -fPIC -c module1.c
$ gcc -shared -o module1.so module1.o
$ gcc -fPIC -c module2.c
$ gcc -shared -o module2.so module2.o
|
Флаг`-fPIC' - 'Position Independent Code', говорит о том , что код будет находиться
в относительном адресном пространстве , т.е. может быть размещен в памяти где угодно
и загрузчик обязан позаботиться во время загрузки о переадресации.
Флаг `-shared' говорит о том , что этот модуль можеть быть прилинкован к другому
исполняемому файлу.
Расширение .so также не должно вводить нас в заблуждение : модуль не может быть прилинкован
с помощью опции gcc `-l' .
4. Использование загрузчика
Команды для использования плагинов и вывод :
$ ./loader module1.so
I am module one!
$ ./loader module2.so
I am module two!
|
5. Добавление функций
Формат ELF (Executable and Linkable Format) генерит 2 секции - .init and .fini -
которые могут включать код , выполняемый перед загрузкой и после згрузки модуля-
т.е. до и после выполнения функции main().
В этих секциях можно проинициализировать необходимые переменные
такие как тип интерфейса плагина .
В данном случае используется флаг компилятора gcc `__attribute__' .
|
__attribute__ ((constructor)) void init()
{
/*код будет выполнен после того,как команда dlopen() загрузит модуль*/
}
__attribute__ ((destructor)) void fini()
{
/*код выполняется перед выгрузкой модуля - dlclose()*/
}
Имена init() и fini() необязательны,
Существует также запрет на использование зарезервированных слов :
_init, _fini, _start и _end.
Для вывода полного списка функций и переменных можно использовать команду
'nm' для бинарника .
|
3xe | При использовании init и fini может возникнуть трабла в : unresolved __dso_handle.
Есть решение: использовать паттерн Proxy:
// implementation of C-style interface to export from dl
extern "C" {
class proxy {
public:
proxy() {
lpCheckLocalCPU = new CheckLocalCPU();
//cout << "proxy::proxy is called" << endl;
}
~proxy() {
//cout << "proxy::~proxy is called" << endl;
delete lpCheckLocalCPU;
}
IPlugin *GetPointer() {
return lpCheckLocalCPU;
}
private:
IPlugin *lpCheckLocalCPU;
};
proxy p;
IPlugin *GetPointer() {
return p.GetPointer();
}
}
2006-01-20 14:48:35 | 3xe | При использовании init и fini может возникнуть трабла в <iostream> : unresolved __dso_handle. 2006-01-20 14:51:36 | 3xe | а потом создать стековый объект Proxy 2006-01-20 14:56:44 | |
|
|