1: / * usehello.c * /
2:
3: #include "libhello.h"
4:
5: int main(void) {
6: print_hello();
7: return 0;
8: }
Содержимое libhello.h оставлено в качестве упражнения для самостоятельной проработки. Для того чтобы скомпилировать и воспользоваться этой библиотекой без ее инсталляции в системе, выполните перечисленные ниже шаги.
1. С использованием флажка -fPIC соберите объектный файл совместно используемой библиотеки:
gcc -fPIC -Wall -g -с libhello.c
2. Скомпонуйте libhello с библиотекой С для достижения лучших результатов во всех системах:
gcc -g -shared -Wl, -soname,libhello.so.0 -о libhello.so.0.0 libhello.о -lc
3. Создайте ссылку из soname на библиотеку:
ln -sf libhello.so.0.0 libhello.so.0
4. Создайте ссылку для использования компоновщиком при компиляции приложений с опцией -lhello:
ln -sf libhello.so.0 libhello.so
5. С помощью флажка -L. укажите компоновщику на необходимость поиска библиотек в текущем каталоге, а с помощью -lhello определите, с какой библиотекой выполнять компоновку:
gcc -Wall -g -с usehello.c -о usehello.o
gcc -g -о usehello usehello.o -L. -lhello
(В этом случае приложение будет компоноваться, даже если вы инсталлируете библиотеку в системе вместо того, чтобы оставить ее в текущем каталоге.)
6. Теперь запустите usehello:
LD_LIBRARY_PATH=$(pwd) ./usehello
Переменная окружения LD_LIBRARY_PATH указывает системе места, где следует искать библиотеки (более детальная информация представлена в следующем разделе). Конечно, по желанию можно установить libhello.so.* в /usr/lib и избежать настройки переменной окружения LD_LIBRARY_PATH.
8.6. Работа с совместно используемыми библиотеками
Самый легкий способ работы с совместно используемыми библиотеками — игнорировать тот факт, что она совместная. Компилятор С автоматически задействует совместно используемые библиотеки вместо статических, если ему явно не указано обратное.
Тем не менее, существуют и три других способа взаимодействия с совместно используемыми библиотеками. Первый способ, явно загружающий и выгружающий библиотеки из программы во время ее работы, называется динамической загрузкой и рассматривается в главе 27. Два других способа описаны ниже.
8.6.1. Использование деинсталлированных библиотек
После запуска программы динамический загрузчик обычно ищет необходимые программе библиотеки в кэше (/etc/ld.so.cache, созданном ldconfig) библиотек, которые находятся в каталогах, записанных в /etc/ld.so.conf. Однако если установлена переменная окружения LD_LIBRARY_PATH, поиск осуществляется сначала в каталогах, перечисленных в ней. Это значит, что если вы хотите использовать измененную версию библиотеки С при работе с определенной программой, эту библиотеку можно поместить в любой каталог и соответствующим образом изменить LD_LIBRARY_PATH. Например, некоторые версии браузера Netscape, скомпонованные с версией 5.2.18 библиотеки С, не будут работать вследствие ошибки сегментации при запуске со стандартной библиотекой С 5.3.12. Это происходит из-за более строгой политики malloc(). Многие помещают копию библиотеки С 5.2.18 в отдельный каталог, например, /usr/local/netscape/lib/, переносят туда исполняемый файл браузера Netscape и заменяют /usr/local/bin/netscape сценарием оболочки, который выглядит примерно так:
#!/bin/sh
export LD_LIBRARY_PATH=/usr/local/netscape/lib:$LD_LIBRARY_PATH
exec /usr/local/netscape/lib/netscape $*
8.6.2. Предварительная загрузка библиотек
В некоторых случаях вместо замены целой библиотеки совместно использования возникает необходимость замены лишь нескольких функций. Вследствие того, что динамический загрузчик выполняет поиск функций, начиная с первой загруженной библиотеки, и продолжает искать в порядке очереди среди массы библиотек, было бы удобно иметь возможность помещать альтернативную библиотеку в начало списка для замены только необходимых функций.
Пример может служить zlibc. Эта библиотека заменяет файловые функции библиотеки С функциями, которые работают со сжатыми файлами. При открытии файла zlibc ищет как запрашиваемый файл, так и gzip-версию файла. Если запрашиваемый файл существует, zlibc в точности воспроизводит функцию библиотеки С, но если файла нет, а вместо него обнаруживается gzip-версия, библиотека распаковывает gzip-файл безо всякого уведомления приложения. Связанные с ней ограничения описаны в документации, zlibc позволяет значительно увеличить количество свободного пространства на диске, разумеется, за счет снижения скорости. Существуют два способа предварительной загрузки библиотеки. Для действия только на определенные программы, можно установить переменную окружения для необходимых случаев:
LD_PRELOAD=/lib/libsomething.o exec /bin/someprogram $*
Кроме того, как и с zlibc, может возникнуть потребность предварительно загрузить библиотеку для всех программ в системе. Самый простой способ для этого — добавить в файл /etc/ld.so.preload строку, которая указывает библиотеку, подлежащую загрузке. Для случая zlibc строка будет выглядеть следующим образом:
/lib/uncompress.о
Глава 9
Системное окружение Linux
В этой главе рассматривается процесс запроса системных служб, включая низкоуровневые средства ядра и высокоуровневые возможности библиотек.
9.1. Окружение процесса
Как подробно описано в главе 10, в каждом выполняющемся процессе есть переменные окружения. Переменные окружения представляют собой пары "имя-значение", и некоторые из них представляют ценность для программистов на языке С. (Многие переменные в первую очередь используются при программировании оболочки, как ускоренные альтернативы запуску программ, вызывающих функции библиотек; в этой книге они описываться не будут.)
EDITOR или VISUAL При установке EDITOR или VISUAL у пользователя появляется возможность выбирать текстовый редактор для редактирования текстового файла. Использование двух различных переменных объясняется тем, что когда-то EDITOR применялась для телетайпной машины, a VISUAL — для полноэкранного терминала. LD_LIBRARY_PATH Обеспечивает разделенные двоеточиями пути к каталогам, в которых следует искать библиотеки. Обычно эту переменную устанавливать не нужно, поскольку в системном файле /etc/ld.so.conf есть вся необходимая информация. Модифицировать его в своих программах вряд ли придется; эта переменная предоставляет информацию для системного компоновщика времени выполнения, ld.so. Однако, как было описано в главе 8, LD_LIBRARY_PATH может оказаться полезной при разработке совместно используемых библиотек. LD_PRELOAD Перечисляет библиотеки, которые должны быть загружены для переопределения символов в системных библиотеках. LD_PRELOAD, как и LD_LIBRARY_PATH, более подробно описана в главе 8. PATH Предоставляет разделенный двоеточиями путь к каталогам, где следует искать исполняемые программы для запуска. Следует отметить, что в Linux (как и во всех вариантах Unix), в отличие от некоторых операционных систем, не принят автоматический поиск исполняемого файла в текущем каталоге. Для этого путь должен включать каталог . (точка). Более подробно о работе с этой переменной рассказывается в главе 10. TERM Предоставляет информацию о типе терминала, установленного у пользователя; это определяет способ позиционирования символов на экране. Более подробно об этом читайте в главах 24 и 21.
9.2. Системные вызовы
В этой книге практически повсеместно упоминаются системные вызовы, которые являются фундаментальными для программного окружения. На первый взгляд, они выглядят как обычные вызовы функций С. И это не случайно; они представляют собой специальную разновидность вызовов функций. Чтобы понять различия, нужно иметь общее представление о структуре операционной системы. Несмотря на то что операционная система Linux состоит из множества фрагментов кода (утилиты, библиотеки, приложения, программные библиотеки, драйверы устройств, файловые системы, управление памятью и так далее), все эти кодовые фрагменты работают в одном из двух контекстов: режиме пользователя или режиме ядра.
При разработку программы написанный код работает в режиме пользователя (user mode). Драйверы устройств и файловые системы, наоборот, работают в режиме ядра (kernel mode). В пользовательском режиме программы тщательно защищены от повреждений, вызванных их взаимодействием друг с другом или остальной частью системы. Код, работающий в режиме ядра, имеет полный доступ к компьютеру и может делать или же разрушать все, что угодно.