Библиотека сайта rus-linux.net
| Цилюрик О.И. Linux-инструменты для Windows-программистов | ||
| Назад | Компиляция и сборка приложений | Вперед |
Конструктор и деструктор
Посмотрим внешние имена (для связывания) созданной в предыдущем примере разделяемой библиотеки:
$ nm libhello.so ... 00000498 T _fini 000002ec T _init ... 00000430 T put_my_msg ...
Мы видим ряд имён (и как раз типа T для внешнего
связывания), одно из которых put_my_msg
— это имя нашей функции. Два других имени — присутствуют
в любой разделяемой библиотеке: функция _init()
вызывается при загрузке библиотеки в память (например, для
инициализации некоторых структур данных), а _fini()
- при выгрузке библиотеки (деструктор).
Примечание: Наличие функций конструктора и
деструктора (_init() и _fini())
является общим свойством всех файлов формата ELF, к которому
относятся и исполнимые файлы Linux, и файлы динамических разделяемых библиотек.
Эти функции (конструктор и деструктор библиотеки)
могут быть переопределены из вашего пользовательского кода создания
библиотеки. Для этого перепишем динамическую библиотеку из наших
предыдущих примеров (каталог init архива libraries.tgz):
hello_child.c :
#include "../hello_child.h"
#include <sys/time.h>
static mark_time( void ) {
struct timeval t;
gettimeofday( &t, NULL );
printf("%02d:%06d : ", t.tv_sec % 100, t.tv_usec );
}
static mark_func( const char *f ) {
mark_time();
printf( "%s\n", f );
}
void _init( void ) {
mark_func( __FUNCTION__ );
}
void _fini( void ) {
mark_func( __FUNCTION__ );
}
int put_my_msg( char *messg ) {
mark_time();
printf( "%s\n", messg );
return -1;
}
Но просто собрать такую библиотеку не получится:
$ gcc -c -fpic -fPIC -shared hello_child.c -o hello_child.o $ gcc -shared -o libhello.so hello_child.o ... hello_child.c:(.text+0x0): multiple definition of `_init' /usr/lib/gcc/i686-redhat-linux/4.4.4/../../../crti.o:(.init+0x0): first defined here hello_child.o: In function `_fini': hello_child.c:(.text+0x26): multiple definition of `_fini' /usr/lib/gcc/i686-redhat-linux/4.4.4/../../../crti.o:(.fini+0x0): first defined here ...
Совершенно естественно, так как мы пытались повторно переопределить имена, уже определенные в стартовом объектном коде. Желаемого результата мы достигнем сборкой с опциями, как показано в следующем сценарии сборки:
Makefile :
TARGET = hello
CHILD = $(TARGET)_child
LIB = lib$(TARGET)
TARGET1 = $(TARGET)_d
TARGET2 = $(TARGET)_a
all: $(LIB) $(TARGET1) $(TARGET2)
$(LIB): $(CHILD).c ../$(CHILD).h
gcc -c -fpic -fPIC -shared $(CHILD).c -o $(CHILD).o
gcc -shared -nostartfiles -o $(LIB).so $(CHILD).o
rm -f *.o
$(TARGET1): $(TARGET1).c $(LIB)
gcc $< -Bdynamic -ldl -L./ -l$(TARGET) -o $@
$(TARGET2): $(TARGET2).c $(LIB)
gcc $< -Bdynamic -L./ -l$(TARGET) -o $@
- здесь как TARGET1 (файл hello_d)
собирается приложение, самостоятельно подгружающее динамическую
библиотеку libhello.so по требованию, а как TARGET2
(файл hello_a) - приложение, опирающееся на автоматическую загрузку библиотек
средствами системы. Сами исходные коды приложений теперь имеют вид:
hello_a.c :
#include "../hello_child.h"
int main( int argc, char *argv[] ) {
int res = put_my_msg( (char*)__FUNCTION__ );
return res;
};
hello_d.c :
#include <dlfcn.h>
#include "../hello_child.h"
typedef int (*my_func)( char* );
int main( int argc, char *argv[] ) {
// Открываем совместно используемую библиотеку
void *dl_handle = dlopen( "./libhello.so", RTLD_LAZY );
if( !dl_handle ) {
printf( "ERROR: %s\n", dlerror() );
return 3;
}
// Находим адрес функции в библиотеке
my_func func = dlsym( dl_handle, "put_my_msg" );
char *error = dlerror();
if( error != NULL ) {
printf( "ERROR: %s\n", dlerror() );
return 4;
}
// Вызываем функцию по найденному адресу
int res = (*func)( (char*)__FUNCTION__ );
// Закрываем библиотеку
dlclose( dl_handle );
return res;
};
Теперь собираем всё это вместе и приступаем к опробованию:
$ make gcc -c -fpic -fPIC -shared hello_child.c -o hello_child.o gcc -shared -nostartfiles -o libhello.so hello_child.o rm -f *.o gcc hello_d.c -Bdynamic -ldl -L./ -lhello -o hello_d gcc hello_a.c -Bdynamic -L./ -lhello -o hello_a $ export LD_LIBRARY_PATH=`pwd` $ ./hello_a 65:074290 : _init 65:074387 : main 65:074400 : _fini $ ./hello_d 68:034516 : _init 68:034782 : main 68:034805 : _fini
Смысл приложений в том, что каждая из функций, в порядке их вызова, выводи своё имя и метку времени, когда она вызвана (число после двоеточия — микросекунды, перед — секунды).
| Предыдущий раздел: | Оглавление | Следующий раздел: |
| Как это всё работает? | Подмена имён |
