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

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


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

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


Сегодня из рассылки Вы _НЕ_ узнаете, что ее существование "не оправдывается"...
Также Вы ни за что _НЕ_ узнаете, что такое Subscribe.RU "изнутри"...
Ну, и конечно же Вы _НЕ_ узнаете, что такое сила мысли и желания...


Вы только узнаете, что должны узнать!!! Продолжаем ковыряться с
библиотеками. На подходе "динамические библиотеки" !!!

ВУАЛЯ !

Создание динамической библиотеки
===================================

Как мы уже говорили раньше, динамические библиотеки немного лучше статических,
но их использование более сложное. И не из-за того, что процесс загрузки
программы замедляется. Проблемы начинаются уже на этапе компиляции :)

Для начала стоит сказать, что объектный файл создаваемый нашим проверенным
способом вовсе не подходит для динамических библиотек. Связано это с тем,
что все объектные файлы создаваемые обычным образом не имеют представления
о том в какие адреса памяти будет загружена использующая их программа.
Несколько различных программ могут использовать одну библиотеку, и каждая
из них располагается в различном адресном пространстве. Поэтому требуется,
чтобы переходы в функциях библиотеки (операции goto на ассемблере)
использовали не абсолютную адресацию, а относительную. То есть генерируемый
компилятором код должен быть независимым от адресов, такая технология
получила название PIC - Position Independent Code. В компиляторе gcc
данная возможность включается ключом -fPIC.

Теперь компилирование наших файлов будет иметь вид:

dron:~# gcc -fPIC -c f1.c
dron:~# gcc -fPIC -c f2.c

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

dron:~# gcc -shared -o libfsdyn.so f1.o f2.o

В результате получим динамическую библиотеку libfsdyn.so, которая по моей
задумке будет динамической версией библиотеки libfs.a, что видно из
названия :) Теперь, чтобы компилировать результирующий файл с
использованием динамической библиотеки нам надо собрать файл командой:

dron:~# gcc -с main.с
dron:~# gcc main.o -L. -lfsdyn -o rezultdyn

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

На этом фокусы не кончаются, если Вы сейчас попробуете запустить файл
rezultdyn, то получите ошибку:

dron:~# ./rezultdyn
 ./rezultdyn: error in loading shared libraries: libfsdyn.so: cannot open
shared object file: No such file or directory
dron:~#

Это сообщение выдает динамический линковщик. Он просто не может найти файл
нашей динамической библиотеки. Дело в том, что загрузчик ищет файлы динамических
библиотек в известных ему директориях, а наша директория ему не известна.
Но это мы чуть отложим, потому что это достаточно сложный вопрос.

А сейчас стоит поговорить еще об одном моменте использования библиотек. Я
специально создал динамическую библиотеку с названием fsdyn, чтобы она
отличалась от названия статической библиотеки fs. Дело в том, что если у
Вас две библиотеки статическая и динамическая с одинаковыми названиями, то
есть libfs.a и libfs.so, то компилятор всегда будет использовать
динамическую библиотеку.

Связано это с тем, что в ключе -l задается часть имени библиотеки, а
префикс lib и окончание .a или .so приставляет сам компилятор. Так вот
алгоритм работы компилятора таков, что если есть динамическая библиотека,
то она используется по умолчанию. Статическая же библиотека используется
когда компилятор не может обнаружить файл .so этой библиотеки. Во всей
имеющейся у меня документации пишется, что если использовать ключ -static,
то можно насильно заставить компилятор использовать статическую библиотеку.
Отлично, попробуем...

dron:~# gcc -staticmain.o -L. -lfs  -o rez1

Как бы я не пробовал играть с позицией ключа -static, результирующий файл
rez1 получается размером в 900 Кб. После применения программы strip размер
ее уменьшается до 200 Кб, но это же не сравнить с тем, что наша первая
статическая компиляция давала программу размером 10 Кб. А связано это с
тем, что любая программа написанная на C/C++ в Linux использует стандартную
библиотеку "C" library, которая содержит в себе определения таких функций,
как printf(), write() и всех остальных. Эта библиотека линкуется к файлу
как динамическая, чтобы все программы написанные на C++ могли использовать
единожды загруженные функции. Ну, а при указании ключа -static компилятор
делает линковку libc статической, поэтому размер кода увеличивается на
все 200 Кб.

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


Использование динамических библиотек
=======================================
Мы с Вами обнаружили, что запуск программ, скомпилированных вместе с
динамическими библиотеками, вызывает ошибку:

dron:~# ./rezultdyn
 ./rezultdyn: error in loading shared libraries: libfsdyn.so: cannot open
shared object file: No such file or directorydron:/#

Это сообщение выдает загрузчик динамических библиотек(динамический
линковщик - dynamic linker), который в нашем случае не может обнаружить
библиотеку libfsdyn.so. Для настройки динамического линковщика существует
ряд программ.

Первая программа называется ldd. Она выдает на экран список динамических
библиотек используемых в программе и их местоположение. В качестве параметра
ей сообщается название обследуемой программы. Давайте попробуем использовать
ее для нашей программы rezultdyn:

dron:~# ldd rezultdyn
        libfsdyn.so => not found
        libc.so.6 => /lib/libc.so.6 (0x40016000)
        /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
dron:~#

Как видите все правильно. Программа использует три библиотеки:

*  libc.so.6 - стандартную библиотеку функций языка C++.
*  ld-linux.so.2 - библиотеку динамической линковки программ ELF формата.
*  libfsdyn.so - нашу динамическую библиотеку функций.

Нашу библиотеку она найти не может. И правильно! Динамический линковщик
ищет библиотеки только в известных ему каталогах, а каталог нашей программы
ему явно не известен.

Для того, чтобы добавить нашу директорию с библиотекой в список известных
директорий надо подредактировать файл /etc/ld.so.conf. Например, у меня
этот файл состоит из таких строк:

dron:~# cat /etc/ld.so.conf
/usr/X11R6/lib
/usr/i386-slackware-linux/lib
/usr/i386-slackware-linux-gnulibc1/lib
/usr/i386-slackware-linux-gnuaout/lib
dron:~#

Во всех этих директории хранятся всеми используемые библиотеки. В этом
списке нет лишь одной директории - /lib, которая сама по себе не нуждается
в описании, так как она является главной. Получается, что наша библиотека
станет "заметной", если поместить ее в один их этих каталогов, либо
отдельно описать в отдельном каталоге. Давайте для теста опишем, добавим
строку в конец файла ld.so.conf:
/root

У меня этот файл валяется в домашнем каталога пользователя root, у Вас он
может быть в другом месте. Теперь после этого динамический линковщик будет
знать где можно найти наш файл, но после изменения конфигурационного файла
ld.so.conf необходимо, чтобы система перечитала настройки заново. Это
делает программа ldconfig. Пробуем запустить нашу программу:

dron:~# ldconfig
dron:~# ./rezultdyn
f1() = 25
f2() = 10
dron:~#

Как видите все заработало :) Если теперь Вы удалите добавленную нами
строку и снова запустите ldconfig, то данные о расположении нашей
библиотеки исчезнут и будет появляться таже самая ошибка.

Но описанный метод влияет на всю систему в целом и требует доступа
администратора системы, т.е. root. А если Вы простой пользователь без
сверх возможностей ?!

Для такого случая есть другое безболезненное решение. Это использование
специальной переменной среды LD_LIBRARY_PATH, в которой перечисляются все
каталоги содержащие пользовательские динамические библиотеки. Для того,
чтобы установить эту переменную в командной среде bash надо набрать всего
несколько команд. Для начала посмотрим есть ли у нас такая переменная
среды:

dron:~# echo $LD_LIBRARY_PATH

У меня в ответ выводится пустая строка, означающая, что такой переменной
среды нет. Устанавливается она следующим образом:

dron:~# LD_LIBRARY_PATH=/root
dron:~# export LD_LIBRARY_PATH

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

dron:~# LD_LIBRARY_PATH=/root:${LD_LIBRARY_PATH}
dron:~# export LD_LIBRARY_PATH

Если Вы обнулите эту переменную, то снова библиотека перестанет работать:

dron:~# LD_LIBRARY_PATH=""
dron:~# export LD_LIBRARY_PATH
dron:~# ./rezultdyn
 ./rezultdyn: error in loading shared libraries: libfsdyn.so: cannot open
shared object file: No such file or directory
dron:~#

Вы также параллельно можете зайти в систему под другим пользователем или
даже тем же самым, но если Вы захотите просмотреть значение LD_LIBRARY_PATH,
то увидите ее прежнее значение. Это означает, что два разных пользователя
Linux не могут влиять на работу друг друга, а это и есть самое главное
хорошее отличие систем Unix от большинства других систем.


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

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

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

В избранное