Здравствуйте.
Сегодня мы поговорим о библиотеках в Linux (подозреваю также, что многие описанные здесь вещи возможны и в других *nix-операционных системах, но это требует проверки :-) ).
Мы рассмотрим:
1) Статические библиотеки (создание с помощью Assembler, C/C++; подключение и использование в программах на Assembler,C/C++);
2) Динамические библиотеки (создание с помощью Assembler, C/C++; подключение и использование в программах на C/C++l, Python).
----------------------------------------------------------------------------------------------------------------
Часть 1.
Статические библиотеки.
1) Статическая библиотека - это такая библиотека, которая связывается (линкуется) с программой в момент компиляции оной. При этом объектный код библиотеки помещается в исполняемый файл программы. С этой точки зрения статическая библиотека похожа на исходный код программы, с которой она связывается, за исключением того, что библиотека компилируется "кем-то еще" и программист, использующий библиотеку, имеет дело исключительно только с результатом этой компиляции.
В Linux, как правило, файл-статическая_библиотека имеет расширение ".a"
2) Статические библиотеки на языке C.
Исходный код библиотеки:
######################
#include <stdio.h>
extern int hello()
{
printf("Hello world! I'm static library\n");
return 0;
};
######################
Сохраните его в файле static.c
Ключевое слово extern необходимо для того, чтобы функция была видна в программе.
Теперь скомпилируем (! без линковки) библиотеку:
gcc -c static.c -o static.o
(на выходе имеем файл static.o, содержащий объектный код нашей библиотеки)
ar rc libMY_STATIC.a static.o
ar упаковывает несколько (! Это важно. Дело не ограничивается только одним объектным файлом) объектных файлов в одну статическую библиотеку. Статическая библиотека имеет расширение ".a", при этом ее название должно начинаться с "lib" (дань традиции).
Параметры ar:
r - предписывает заменять старые версии объектных файлов новыми - необходим для переупаковки библиотеки;
c - создать статическую библиотеку, если та еще не существует.
Проиндексируем функции внутри библиотеки для более быстрой линковки:
ranlib libMY_STATIC.a
Итак, мы получили статическую библиотеку libMY_STATIC.a.
Теперь попытаемся использовать библиотеку в нашей программе:
Исходный текст программы (C):
######################
#include <stdio.h>
int main()
{
int x = hello();
printf("Return code: %d\n",x);
return 0;
}
######################
Сохраните его в файле program1.c
Способы связывания библиотеки и программы:
- Скомпилируем и слинкуем (в том числе с нашей библиотекой) нашу программу:
gcc program1.c libMY_STATIC.a
(предполагается, что в качестве аргумента gcc будут переданы полные пути (!) к вашим библиотекам)
И запустим:
./a.out
На выходе получим:
Hello world! I'm static library
Return code: 0
Отлично!
- Скомпилируйте с помощью команды:
gcc program1.c -L. -lMY_STATIC -o a1.out
Смысл аргументов:
-L
Запустите файл a1.out на выполнение и удостовертесь, что результаты те же, что и в предыдущем пункте.
- Видоизменим предыдущий способ - уберем аргументы "-L":
В начале проверим значение переменной LD_LIBRARY_PATH и содержимое файла /etc/ld.so.conf:
echo $LD_LIBRARY_PATH ; cat /etc/ld.so.conf
На экране появился некоторый список каталогов - это те каталоги, в которых система ищет библиотеки при их линковке с программой (еще к таким каталогам относятся:
/lib
/usr/lib
. Поместите libMY_STATIC.a в один из этих каталогов:
(Я, к примеру, засуну нашу библиотеку в каталог /usr/lib):
su -c 'cp libMY_STATIC.a /usr/lib'
(в Ubuntu -
sudo cp libMY_STATIC.a /usr/lib
)ldconfig
(ldconfig обновляет кеш данных о библиотеках линковщика)
Теперь скомпилируем и запустим нашу программу:
gcc program1.c -lMY_STATIC -o a2.out
./a2.out
Результат:
Hello world! I'm static library
Return code: 0
Бинго! Кстати, таким вот способом вы можете подключать к своей программе любые статические библиотеки из приведенных выше каталогов.
* Бывает полезно определить все прототипы функций библиотеки в некотором заголовочном файле, который будет потом включаться в вашу программу. Это не обязательно, но удобно.
3) Статические библиотеки на языке Assembler.
Представьте, что вам необходимо оптимизировать выполнение некоторых действий в вашей программе. Разумеется, вы может применить ключевое слово asm (если пишите программу на C/C++), но лучшим решением будет создание оптимизированной вами библиотеки на языке Assembler и подключение ее к вашей программе. Давайте попробуем:
*Кстати, углубляться в процесс компиляции библиотеки и ее линковки с вашей программой я не буду (!). Этот процесс идентичен полностью (!) тому же процессу для библиотек, написанных на языке C.
Итак, имеем вот такую программу:
######################
#include <stdio.h>
int main()
{
printf("Hello world!\n");
return 0;
};
######################
Сохраните ее в файле program2.c
Скомпилируйте ее и запустите:
gcc program2.c
./a.out
Я получил:
Hello world!
Отлично.
Я привел этот пример, чтобы показать действительно возможность оптимизации программы с помощью библиотеки на Assembler'е. Вы можете заметить, что вызов printf в main() не оптимален, т.к. printf, по крайней мере, один раз использует цикл while для поиска вхождений конструкций "%..." в строку. Это не оптимально, т.к. очевидно, что таковых символов у нас нет. Оптимизируем нашу программу с помощью библиотеки на Assebmler'е:
######################
.text
.globl my_printf
my_printf:
movl $4,%eax
xorl %ebx,%ebx
incl %ebx
movl $hw,%ecx
movl $hw_e,%edx
int $0x80
xorl %eax,%eax
ret
.data
hw:
.string "Hello world!\012"
hw_e = . - hw
######################
Сохраните исходный код библиотеки в файле static2.s
Это AT&T наречие Assembler'а.
.globl my_printf
- "my_printf" описывается как глобальная (видимая в других объектных файлах) последовательностьmy_printf:
- начало описание функции my_printfmovl $4,%eax
- поместим 4 в eax (4 - номер системного вызова write)xorl %ebx,%ebx
и incl %ebx
- поместим в ebx единицу - номер STDOUTmovl $message,%ecx
- в ecx запишем адрес начала сообщенияmovl $message_l,%edx
- в edx поместим адрес конца сообщенияint $0x80
- произведем системный вызов writexorl %eax,%eax
- в eax - код возврата (0)ret
- вернемся в вызывающую процедуру.data
- секция данных (разумеется, мы могли бы передавать выводимую строку как параметр, но тогда вычисление ее конца потребовало бы от нас дополнительных усилий, что, согласитесь, лениво :-) )Теперь получим библиотеку:
gcc -c static2.s -o static2.o
ar rc static2.a static2.o
ranlib static2.a
На выходе имеем статическую библиотеку static2.a
Теперь напишем программу, использующую эту статическую библиотеку (язык C):
######################
#include <stdio.h>
int my_printf();
int main()
{
int x = my_printf();
return 0;
}
######################
Сохраните текст программы в файле program3.c
Заметьте, я добавил прототип библиотечной функции для удобства.
Скомпилируем и слинкуем программу с библиотекой, после чего запустим программу на выполнение:
gcc program3.c static2.a
./a.out
На выходе получим:
Hello world!
* Принцип линкования статических библиотек с программами на Assembler'е аналогичен принципу для программ на C. Просто, когда будете использовать статические библиотеки в Assembler'е, помните о соглашениях C по передаче аргументов в функцию и возвращению результата.
4) Статические библиотеки на языке C++.
Принцип создания аналогичен статическим библиотекам на C, но перед каждой экспортируемой функцией не забывайте добавлять:
extern "C"
(экспортировать как функцию на C - т.е. без расширения имен).
* Кстати, используйте g++ вместо gcc, если захотите протестировать приведенные выше примеры.
Подключение к вашей программе аналогично подключению к программе, написанной на C, за исключением необходимости явно добавлять к тексту программы прототипы импортируемых функций в следующем виде:
extern "C" PROTOTYPE
Где PROTOTYPE - прототип импортируемой функции.
* При подключении статических библиотек на C++ к программе на C сопряжено с некоторыми трудностями - т.к. при компиляции и линковки программы необходимо будет также вручную подключить системные библиотеки для реализации функционала, предоставляемого библиотекой Standart C++ сверх того, что предоставляет библиотека Standart C.
----------------------------------------------------------------------------------------------------------------
Часть 2.
Динамические библиотеки (shared).
1) Динамическая библиотека - библиотека, подключаемая к программе в момент выполнения. Это означает, что при создании библиотеки производится не только ее компиляция, но и линковка с другими, нужными ей, библиотеками (!).
Динамические библиотеки полезны в случаях, если:
- Важно не перекомпилировать всю программу, а только перекомпилировать ту часть, которая реализует определенные функции - тогда эти функции выносятся в динамическую библиотеку;
- Важно использовать в программах на C библиотеки, подготовленные на C++ и при этом избежать лишних трудностей с линковкой программы;
- Кроме того, динамические библиотеки позволяют экономить место на жестком диске и в оперативной памяти, если одна и таже библиотека используется несколькими программами.
В Linux, обычно, динамические библиотеки имеют расширение ".so".
2) Подготовим исходный код динамической библиотеки (пример на C++).
Исходный код динамической библиотеки по принципам создания ничем (!) не отличается от исходного кода статических библиотек.
Здесь мы подготовим некоторый пример, который в дальнейшем будем использовать повсеместно во всей части 2.
Итак, исходный код библиотеки (C++):
######################
#include <iostream>
using namespace std;
#include <dynamic.h>
extern "C" int hello()
{
cout<<"Hello world!\n I'm function hello()"<<endl;
return 0;
}
######################
Сохраните приведенный код в файле dynamic.cpp.
* Кстати, внутри динамической библиотеки вы можете описать следующие функции:
void _init() - будет вызвана при инициализации динамической библиотеки (загрузки ее в память);
void _fini() - будет вызвана при выгрузке из памяти динамической библиотеки.
3) Компиляция и линковка динамических библиотек.
Давайте получим динамическую библиотеку:
Получим файл с объектным кодом:
g++ -fPIC -c dynamic.cpp -o dynamic.o
(используйте gcc для программ на С и Assembler'е)
Здесь:
-fPIC - использовать относительную адресацию в переходах подпрограмм - во избежание конфликтов при динамическом связывании
А теперь из объектного файла получим библиотеку:
g++ -shared -olibdynamic.so dynamic.o
(используйте gcc для программ на С и Assembler'е)
libdynamic.so - имя результирующей библиотеки;
-shared - предписывает создать динамическую (т.е. "разделяемую") библиотеку.
* Именуйте динамические библиотеки следующим способом:
libNAME.so
Это традиция ;-)
Итак, на выходе мы имеем libdynamic.so - нашу динамическую библиотеку.
4) Использование динамической библиотеки в программе на C/C++.
- Связывание с библиотекой во время компиляции программы (C/C++):
------ Подготовим исходный код нашей программы:
(С)
######################
int main()
{
int x = hello();
printf("Return code: %x\n",x);
return 0;
}
######################
Сохраните его в файле Dprogram1.c
ИЛИ
(С++)
######################
#include <stdio.h>
extern "C" int hello();
int main()
{
int x = hello();
printf("Return code: %x\n",x);
return 0;
}
######################
Сохраните его в файле Dprogram1.cpp
(единственное отличие, как вы можете заметить, в ключевом слове extern - см. часть 1 пункт 4)
------ Теперь добьемся того, чтобы система смогла найти нашу библиотеку. Поместим libdynamic.so в один из каталогов:
из списка:
echo $LD_LIBRARY_PATH
/lib
/usr/lib
или из списка:
cat /etc/ld.so.conf
и выполните потом "ldconfig
"
------ И, наконец, скомпилируем программу и слинкуем ее с библиотекой:
gcc ИСХОДНИК -lИМЯ_БИБЛИОТЕКИ -o РЕЗУЛЬТИРУЮЩИЙ_БИНАРИК
В нашем случае: gcc Dprogram1.c -L/home/amv/c/libs/ -ldynamic
(используйте g++ для программы на C++)
Запустим на исполнение полученный файл:
./a.out
В итоге должно получится:
Hello world!
I'm function hello()
Return code: 0
- Связывание с библиотекой во время исполнения программы (C/C++):
Разумеется, предыдущий пример неплох. Однако бывает необходимо подключать библиотеку во время выполнения программы. Для этого можно использовать функционал из заголовочного файла .
Исходный код примера (C):
######################
#include <dlfcn.h>
#include <stdio.h>
int main()
{
void *handle = dlopen("libdynamic.so",RTLD_LAZY);
int(*fun)(void) = dlsym(handle,"hello");
int x = (*fun)();
dlclose(handle);
printf("Return code: %d\n",x);
return 0;
};
######################
Сохраните его в файле Dprogram2.c
В dlfcn.h определены следующие функции:
void* dlopen("PATH_AND_NAME",FLAG) - загружает в память динамическую библиотеку с полным именем PATH_AND_NAME и возвращает ее описатель (HANDLE) (NULL в случае неудачи). FLAG - флаги, описанные в "man dlopen";
void* dlsym(HANDLE,"NAME") - возвращает указатель на функцию/переменную, импортируемую из библиотеки;
int dlclose(HANDLE) - выгружает библиотеку из памяти;
const char *dlerror() - получить сообщение о последней возникшей ошибке (NULL - если ошибок не произошло с момента последнего вызова dlerror).
* Посмотрите на досуге вот этот перевод "man dlopen": Привет, OpenNET
Выполняем:
gcc -ldl Dprogram2.c
(используйте g++ для программы на C++)
Запустим на исполнение полученный файл:
./a.out
В итоге должно получится:
Hello world!
I'm function hello()
Return code: 0
* Важно! Нет необходимости помещать библиотеку в один из специальных каталогов, модифицировать переменные окружения и выполнять "ldconfig"
- Использование динамической библиотеки в программе на Python:
Все предельно просто.
------ Поместим libdynamic.so в один из каталогов:
из списка:
echo $LD_LIBRARY_PATH
/lib
/usr/lib
или из списка:
cat /etc/ld.so.conf
и выполните потом "ldconfig"
------ python:
Исходный текст программы на python'е:
######################
#!/usr/bin/python
import ctypes
dl = ctypes.cdll.LoadLibrary("libdynamic.so")
dl.hello()
######################
Модуль ctypes входит в стандартную поставку модулей python версии 2.5 и выше.
----------------------------------------------------------------------------------------------------------------
Фуф. Мы проделали довольно большую работу, но ведь это только верхушка айсберга.
P.S. Я намерено пропустил тему "Perl и динамические библиотеки" - сейчас мне просто лениво разбирать свои старые примеры. Могу только сказать, что за динамические библиотеки в Perl ведает модуль DynaLoader (и не только он), который является, по сути, оболочкой для dlfcn.h. Почитайте perldoc DynaLoader ;-)
UPDATE: 25.07.2008
13 комментариев:
sudo su -c 'cp libMY_STATIC.a /usr/lib'
зачем так?
Упс. Упустил. И правдо нерационально
sudo cp libMY_STATIC.a /usr/lib
Спасибо за наводящий вопрос :-)
Еще бы написали про именование .so .so.0 .so.0.1 и так далее.
Спасибо! Очень познавательно. Особенно порадовал пример на Питоне :)
Реально пинуло меня в нужном направлении
СПАСИБО
Реально пинуло меня в нужном направлении
СПАСИБО
Спасибо. Помогло и не один раз.
Спасибо
О! Спасибище огроменное! Кое чего из сказанного я не знал. Теперь знаю :)
спс, круто
у меня получился вывод hello() но терминал написал при компиляции implicit declaration function hello()
а как компилить динамическую библиотеку с внешними зависимостями? К примеру Xlib не живет одновременно с OCV, поэтому часть функций я вынес в отдельный модуль, который хочу собрать в динамическую библиотеку, чтобы потом вызывает ее функции в другой программе..
Компилирую так:
1. g++ -fPIC -I /usr/include/X11 -lX11 -c main.cpp -o SS_DLL.o
2. g++ -shared -oSS_DLL.so SS_DLL.o
В итоге получаю динамическую либу - SS_DLL.so..
Вроде все гуд!
В основной программе:
int main()
{
void *dll_handle = dlopen("/home/darkf0/SS_DLL.so", RTLD_LAZY);
if (dll_handle != nullptr)
{
typedef img_data* (*GetScreenShot)(void);
GetScreenShot Get_SS = nullptr;
try
{
Get_SS = (GetScreenShot)dlsym(dll_handle,"GetScreenShot");
if (Get_SS != nullptr)
{
img_data *x = Get_SS();
if (x != nullptr)
{
printf("bits_per_pixel: %d\n",x->bits_per_pixel);
delete x;
}
}
} catch (...)
{
printf("Error GetSS\n");
}
dlclose(dll_handle);
}
return 0;
}
На инструкции - img_data *x = Get_SS();
Программа падает с ошибкой:
symbol lookup error: /home/darkf0/SS_DLL.so: undefined symbol: XOpenDisplay
То-есть получается моя динамическая либа собралась без линковки внешний зависимостей.. И как с этим шаманством разобраться я не знаю.
адениум купить семена
адениум и плюмерии
Отправить комментарий