Совместно используемые библиотеки в C под Linux для начинающих. (продолжение) - Начинающим - Shelek
Иногда требуется загрузить какую нибуть библиотеку, явно не компануя её. Например страшное слово плагин. Для этих целей у нас есть dlopen, dlclose, dlsym и dlerror объявленны в dlfcn.h, компилируется с флагом -ldl.

Код:
void *dlopen(const char *filename, int flag);
const char *dlerror(void);
void *dlsym(void *handle, char *symbol);
int dlclose(void *handle);

Функция dlopen открывает файл filename с флагами flag. Вернее откравает она его в том случае, если он еще небыл кем-то открыт в этом случае она увеличивет счетчик ссылок возврашает дискриптор библиотеки. Путь к библиотеки можно не указывать, тогда бибилиотека ищется . dlclose совершает обратную работу уменьшает счетчик и закрывает библиотеку, если её больше не использует.
Функция dlsys возврашает указатель на функцию или переменную с именем symbol для библиотеки Handle.
Функция dlerror возвращает струказатель на строку с описанием ошибки.

Пример:
Код:
void * handle = dlopen("mylib.so", RTLD_LAZY);
void (* test)() = dlsym( handle, "myfunc");
(* test)();
dlclose(handle);

Если библиотека пишется на C++ имеет смысл объявить обще доступные функции со спецификатором extern "C".

Код:
extern "C" void my_func();

Это делается из-за того, что компилятор C++ имеет свойство подменять имена функций своими именами в которых закодированна информация о функции.

Пример:

Листинг testdl.c:
Код:
------------------------------------------------------------
#include <stdio.h>
#include <dlfcn.h>

int main(int argc,char** argv[])
{
/* Проверяем кол-во параметров.
Если чего не так выводим сообщение об ошибке и уходим. */
if (argc<2) {
fprintf(stderr,"Usage: %s libnamen",argv[0]);
exit(1);
}

/* Пытаемся загрузить библиотеку. */
printf("Try to load library %sn",argv[1]);
void * handle = dlopen(argv[1], RTLD_LAZY);
/* Если чего не так выводим сообщение об ошибке и уходим. */
if (handle == NULL) {
fprintf(stderr,"%s",dlerror());
exit(2);
}

printf("Ok! Loaded!n");

/* Пытаемся загрузить функцию библиотеки. */
void (*libfunc)(int) = dlsym(handle, "libfunc");
/* Если чего не так выводим сообщение об ошибке и уходим. */
if (libfunc == NULL) {
fprintf(stderr,"%sn",dlerror());
exit(3);
}

/* Выводим название загруженной библиотеки. */
printf("Loaded library: %s nLib output:n",argv[1]);

/* Вызываем функцию из библиотеки передаем при этом
дискриптор для вывода данных. */
libfunc(stderr);

/* Закрываем библиотеку. */
dlclose(handle);
return 0;
}
------------------------------------------------------------

Листинг mygettime.c:
Код:
------------------------------------------------------------

#include <sys/time.h>
#include <time.h>
#include <stdio.h>
#include <unistd.h>

void libfunc(int fd)
{
struct timeval tv;
struct tm* ptm;
char * time_string[40];

/* Получаем локальное время. */
gettimeofday(&tv, NULL);
ptm = localtime(&tv.tv_sec);

/* Преобразуем в строку. */
strftime(time_string, sizeof(time_string), "%H:%M:%S", ptm);
/* Выводим время. */
fprintf(fd,"Local time: %sn",time_string);
return;
}
------------------------------------------------------------

Листинг myversion.c:
Код:
------------------------------------------------------------
#include <fcntl.h>

void libfunc(int fd)
{
int in_fd;
int rval;
char *buffer=calloc(512,sizeof(char));

/* Открываем файл /etc/issue. */
in_fd = open("/etc/issue",O_RDONLY);
if(in_fd == -1) {
fprintf(fd,"Error open /etc/issuen");
exit(1);
}

/* Читаем из /etc/issue и пишем в fd. */
while ((rval=read(in_fd,buffer,512))!= 0)
fprintf(fd,"%s",buffer);
return;
}
------------------------------------------------------------

Листинг Makefile:
Код:
------------------------------------------------------------
### Конфигурация. ###################################

SOURCES = testdl.c
MODULES = mygettime.so myversion.so

### Правила. ########################################

# Служебный целевой модуль
.PHONY: all clean

# Стандартный целевой модуль: компиляция всех фалов.
all: $(MODULES) testdl

# Удаление всего чего насобирали.
clean:
rm -f $(MODULES) testdl

# Собираем гвавный файл.
testdl: $(SOURCES)
$(CC) $(CFLAGS) -Wl,-export-dynamic -o $@ $^ -ldl

# Собираем библиотеки.
$(MODULES):
%.so: %.c
$(CC) $(CFLAGS) -fPIC -shared -o $@ %body%lt;
------------------------------------------------------------

С файлами надеюсь всё понятно. Testdl.c наша главная программа которая будет использовать наши библиотечки. Файл mygettime.c и myversion.c файлы для библиотек выводят текушее время и версию системы. Makefile это, что бы нам руками всё время команды не писать для компиляции, а набрали make и всё собралось, набрали make clean и всё собраное удалилось.

По тестируем всё это:
Код:
$ make
$ export LD_LIBRARY_PATH=/path_to_current_dir
$ ./testdl mygettime.so && ./testdl myversion.so
Try to load library mygettime.so
Ok! Loaded!
Loaded library: mygettime.so
Lib output:
Local time: 09:44:00
Try to load library myversion.so
Ok! Loaded!
Loaded library: myversion.so
Lib output:

ASPLinux release 9 (Ural)
Kernel 2.4.20-9asp on an i686

Спрашивается, зачем мы передавали в функции stderr, если всё равно идет вывод на экран. а мы за одно посмотрим как перенаправить поток. Есть три стандартаных дескриптора файлов это 0, 1, 2 или stdin, stdout, stderr. Перенаправим в файлик test.log стандартный вывод ошибок.

Код:
$ ./testdl myversion.so 2test.log
Try to load library myversion.so
Ok! Loaded!
Loaded library: myversion.so
Lib output:
$ cat test.log

ASPLinux release 9 (Ural)
Kernel 2.4.20-9asp on an i686

$

Спрашивается - а зачем? Ну даже не знаю. Можно например запустить две программы терминала в графическом режиме. Затем в одной запустить другую программу например компиляцию чего-нибуть и перенаправить вывод ощибок в другое окно, чтобы не путались сообщения об ошибках и сообщения о ходе выполнения. Или в файли можно лог ошибок сохранить. Да малоли чего.

Пример:
Код:
$ echo "Hi, everybody." /dev/pts/1

или

Код:
$ make 2test.log 1/dev/pts/1


Всё!

Автор: LogRus
Information
  • Posted on 31.01.2010 21:43
  • Просмотры: 3929