Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Программирование под Linux #6 (27-02-2002)


Служба Рассылок Subscribe.Ru

Здравствуйте дорогие подписчики рассылки "Программирование под Linux" !!!
 ....----==== http://linux.firststeps.ru/ ====----....


ХОТИТЕ ПОМОЧЬ РАССЫЛКЕ ?! Найдите новых подписчиков !!!
Рассказывайте о ней на форумах, страницах своих и чужих сайтов.
Подписывайте своих друзей, коллег и всех всех всех !!!
По любым вопросам обращайтесь ко мне: dron@mjk.msk.ru

Функции работы с динамическими библиотеками
===============================================

Если Вы подумали, что фокусы с динамическими библиотеками кончились, то Вы
очень сильно ошиблись. До того были цветочки, а ягодки будут сейчас :)

Оказывается, что использовать динамические библиотеки можно не только в
начале загрузки, но и в процессе самой работы программы. Программа сама
может вызывать любые функции из библиотеки, когда ей захочется. Для этого
всего-лишь надо использовать библиотеку dl, которая позволяет линковать
библиотеки "на лету". Она управляет загрузкой динамических библиотек,
вызовом функций из них и выгрузкой после конца работы.

Для использования функций программной работы с динамическими библиотеками
необходимо подключить заголовочный файл:

#include <dlfcn.h>

Чтобы вызывать какие-то функции из динамической библиотеки сначала надо
открыть эту библиотеку (можно сказать "загрузить"). Открывается она функцией:

void *dlopen (const char *filename, int flag);

Параметр filename содержит путь до требуемой библиотеки, а параметр flag задает
некоторые специфические флаги для работы. Функция возвращает указатель на
загруженную библиотеку. В случае любой ошибки возвращается указатель NULL. В
таком случае тест ошибки понятный человеку можно получить с помощью функции
dlerror(). Пока мы не будем задумываться над этим, и я приведу стандартный
код для открытия библиотеки:

void *library_handler;

//.

//загрузка библиотеки

library_handler = dlopen("/path/to/the/library.so",RTLD_LAZY);
if (!library_handler){
 //если ошибка, то вывести ее на экран
 fprintf(stderr,"dlopen() error: %s\n", dlerror());
 exit(1); // в случае ошибки можно, например, закончить работу программы
};

После этого можно работать с библиотекой. А работа эта заключается в
получении адреса требуемой функции из библиотеки. Получить адрес функции
или переменной можно по ее имени с помощью функции:

void *dlsym(void *handle, char *symbol);

Для этой функции требуется адрес загруженной библиотеки handle, полученный
при открытии функцией dlopen(). Требуемая функция или переменная задается
своим именем в переменной symbol.

Закрывается библиотека функцией:

dlclose(void *handle);

При закрытии библиотеки динамический линковщик проверяет счетчик количества
открытий библиотеки, и если она была открыта несколькими программами
одновременно, то она не выгружается до тех пор, пока все программы не
закроют эту библиотеку.

Для примера создадим программу, которая в качестве параметра получает
название функции, которую она будет использовать в работе. Например, это
будут математические функции возведения в степень. Создадим сначала
динамическую библиотеку. Пишем ее код:

double power2(double x){
 return x*x;
};

double power3(double x){
 return x*x*x;
};

double power4(double x){
 return power2(x)*power2(x);
};
//.

Сохраняем его в файл lib.c и создаем динамическую библиотеку libpowers.so
следующими командами:

dron:~# gcc -fPIC -c lib.c
dron:~# gcc -shared lib.o -o libpowers.so

Теперь создаем основную программу в файле main.c:

#include <stdio.h>
/* заголовочный файл для работы с динамическими библиотеками */
#include <dlfcn.h>

int main(int argc, char* argv[]){

 void *ext_library; // хандлер внешней библиотеки
 double value=0;  // значение для теста
 double (*powerfunc)(double x); // переменная для хранения адреса функции

 //загрузка библиотеки
 ext_library = dlopen("/root/libpowers.so",RTLD_LAZY);
 if (!ext_library){
  //если ошибка, то вывести ее на экран
  fprintf(stderr,"dlopen() error: %s\n", dlerror());
  return 1;
 };

 //загружаем из библиотеки требуемую процедуру
 powerfunc = dlsym(ext_library, argv[1]);
 value=3.0;

 //выводим результат работы процедуры
 printf("%s(%f) = %f\n",argv[1],value,(*powerfunc)(value));

 //закрываем библиотеку
 dlclose(ext_library);
};

Код главной программы готов. Требуется его откомпилировать с использованием
библиотеки dl:

dron:~# gcc main.c -o main -ldl

Получим программный файл main, который можно тестировать. Наша программа
должна возводить значение 3.0 в требуемую нами степень, которая задается
названием функции. Давайте попробуем:

dron:~# ./main power2
power2(3.000000) = 9.000000
dron:~# ./main power3
power3(3.000000) = 27.000000
dron:~# ./main power4
power4(3.000000) = 81.000000
dron:~#

Ну, как ?! Круто !!! Мы используем какие-то функции, зная лишь их название.
Представьте открывающиеся возможности для программ, на основе этого метода
можно создавать плагины для программ, модернизировать какие-то части,
добавлять новые возможности и многое другое.



Инициализация динамических библиотек
=========================================

До сих пор мы писали с Вами простые динамические библиотеки, поэтому у нас
не болела голова об инициализации внутренних переменных. А представьте себе
более сложную ситуацию, когда функции библиотеки для работы требуют
правильно инициализированные переменные. Ну, например, для работы функции
нужен буфер или массив.

Специально для таких случаев в библиотеках можно задавать инициализирующую
и деинициализирующую функции:

void _init() - инициализация
void _fini() - деинициализация

Чтобы понять, что к чему, введем в нашей библиотеке lib.c переменную test
и возвращающую ее функцию:

char *test;

char *ret_test(){
 return test;
};

Пишем основную программу main.c. Она очень похожа на предыдущий наш проект,
поэтому можете его модифицировать:

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

int main(){

 void *ext_library;
 double value=0;
 char * (*ret_test)();

 ext_library = dlopen("libtest.so",RTLD_LAZY);
 if (!ext_library){
  fprintf(stderr,"dlopen() error: %s\n", dlerror());
  return 1;
 };

 ret_test = dlsym(ext_library,"ret_test");

 printf("Return of ret_test: \"%s\" [%p]\n",(*ret_test)(),(*ret_test)());

 dlclose(ext_library);
};

После компиляции всего этого хозяйства мы получим результат:

dron:~# gcc -c lib.c -fPIC
dron:~# gcc -shared lib.o -o libtest.so
dron:~# gcc -o main main.c -ldl
dron:~# ./main
Return of ret_test: "(null)" [(nil)]
dron:~#

Как видите переменная test оказалась равной NULL, а нам бы хотелось нечто
другое. Ну, так давайте посмотрим как работают функции _init() и _fini().
Создадим вторую библиотеку lib1.c:

#include <stdlib.h>

char *test;

char *ret_test(){
 return test;
};

void _init(){
 test=(char *)malloc(6);
 if (test!=NULL){
  *(test+0)='d';
  *(test+1)='r';
  *(test+2)='o';
  *(test+3)='n';
  *(test+4)='!';
  *(test+5)=0;
 };
 printf("_init() executed...\n");
};

void _fini(){
 if (test!=NULL) free(test);
 printf("_fini() executed...\n");
};

Теперь пробуем компилировать:

dron:~# gcc -c lib1.c -fPIC
dron:~# gcc -shared lib1.o -o libtest.so
lib1.o: In function _init':
lib1.o(.text+0x24): multiple definition of _init'
/usr/lib/crti.o(.init+0x0): first defined here
lib1.o: In function _fini':
lib1.o(.text+0xc0): multiple definition of _fini'
/usr/lib/crti.o(.fini+0x0): first defined here
collect2: ld returned 1 exit status
dron:~#

Опаньки... Облом. Что же это такое ?! Оказывается кто-то уже использовал
эти функции до нас и программа не может слинковаться. После долгого копания
в нескольких чужих исходниках я получил ответ на этот вопрос. Оказывается,
чтобы избавиться от мешающей библиотеки надо использовать ключ
компилятора -nostdlib. Попробуем:

dron:~# gcc -shared -nostdlib lib1.o -o libtest.so
dron:~#

Смотрите-ка, все прекрасно скомилировалось. Теперь попробуем запустить main:

dron:~# ./main
_init() executed...
Return of ret_test: "dron!" [0x8049c20]
_fini() executed...
dron:~#

Ну как ? Помоему классно. Теперь можно спокойно создавать для работы библиотеки
правильно инициализированные переменные. Однако классные эти штуки - динамические
библиотеки ! А Вы что хотели ? Тот же Windows только на своих DLL и живет. А
Linux ничем не хуже...



Количество подписчиков: 643

Выпуск подготовил: Кузин Андрей (dron@mjk.msk.ru)

http://subscribe.ru/
E-mail: ask@subscribe.ru
Отписаться
Убрать рекламу

В избранное