Search     or:     and:
 LINUX 
 Language 
 Kernel 
 Package 
 Book 
 Test 
 OS 
 Forum 
iakovlev.org

Плагины

Наиболее простой способ обеспечения расширяемости ваших программ - использование модулей , известных как плагины . К числу известных программ такого типа относятся веб-броузеры,плееры : в веб-броузерах например плагины используются для поддержки 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