Упражнение 1.2. Статические библиотеки
В этом упражнении вы создадите свою маленькую библиотеку, содержащую две функции, и затем используете одну из функций в примере программы. Функции называются fred и bill и просто выводят приветствия.
1. Сначала создайте отдельные исходные файлы (как не удивительно, названные fred.c и bill.c) для каждой функции.
Далее приведен первый из них:
#include <stdio.h>
void fred(int arg) {
printf("fred: you passed %d\n", arg);
}
А это второй:
#include <stdio.h>
void bill(char *arg) {
printf("bill: you passed %s\n", arg);
}
2. Вы можете отдельно откомпилировать эти функции и создать объектные файлы, готовые к включению в библиотеку. Для этого запустите компилятор С с опцией -с, которая помешает компилятору создать законченную программу. Попытка создать законченную программу окажется безуспешной, т.к. вы не определили функцию с именем main.
$ gcc -с bill.с fred.c
$ ls *.o
bill.о fred.о
3. Теперь напишите программу, вызывающую функцию bill. Прежде всего, хорошо бы создать заголовочный файл для вашей библиотеки. В нем будут объявлены функции из вашей библиотеки, и он будет включаться во все приложения, которые захотят применить вашу библиотеку. В файлы fred.c и bill.c тоже хорошо бы включить заголовочный файл, чтобы помочь компилятору обнаружить любые ошибки.
/*
Это файл lib.h. В кем объявлены пользовательские функции fred and bill
*/
void bill(char *);
void fred(int);
4. Вызывающая программа (program.с) может быть очень простой. Она включает заголовочный файл и вызов из библиотеки одной из функций.
#include <stdlib.h>
#include "lib.h"
int main() {
bill("Hello World");
exit(0);
}
5. Теперь можно откомпилировать и протестировать программу. Для этого задайте компилятору явно объектные файлы и попросите его откомпилировать ваш файл и связать его с ранее откомпилированным объектным модулем bill.o.
$ gcc -с program.с
$ gcc -о program program.о bill.о
$ ./program
bill: we passed Hello World
$
6. Для создания архива и включения в него ваших объектных файлов используйте программу ar. Программа называется ar, поскольку она создает архивы или коллекции отдельных файлов, помещая их все в один большой файл. Имейте в виду, что программу ar можно применять для создания архивов из файлов любого типа. (Как многие утилиты UNIX, ar - универсальное средство.)
$ ar crv libfоо.a bill.о fred.о
а - bill.о а - fred.о
7. Библиотека создана, и в нее добавлены два объектных файла. Для того чтобы успешно применять библиотеку в некоторых системах, в особенности в производных от Berkeley UNIX, требуется создать для библиотеки индекс содержимого архива или список вложенных в библиотеку функций и переменных (table of contents). Сделайте это с помощью команды ranlib. В ОС Linux при использовании программных средств разработки GNU этот шаг не является необходимым (но и не приносит вреда).
$ ranlib libfoo.a
Теперь ваша библиотека готова к использованию. Вы можете добавить следующий список файлов, которые должен обработать компилятор для создания вашей программы:
$ gcc -о program program.о libfоо.а
$ ./program
bill: we passed Hello world
Можно было бы применить для доступа к библиотеке флаг -l, но т.к. она хранится не в одном из стандартных каталогов, вы должны сообщить компилятору место поиска с помощью флага -L следующим образом:
$ gcc -о program .program.о -L. -lfoo
Опция -L заставляет компилятор искать библиотеки в текущем каталоге (.). Опция -lfoo сообщает компилятору, что нужно использовать библиотеку с именем libfoo.a (или совместно используемую библиотеку libfoo.so, если она есть). Для того чтобы посмотреть, какие функции включены в объектный файл, библиотеку или исполняемую программу, можно применить команду nm. Если вы взглянете на файлы program и libfoo.a, то увидите, что библиотека содержит обе функции: fred и bill, а файл program - только функцию bill. Когда создается программа, в нее включаются из библиотеки только те функции, которые ей действительно нужны. Вставка заголовочного файла, содержащего объявления всех функций библиотеки, не вызывает включения в конечную программу целиком всей библиотеки.
Если вы знакомы с разработкой программ в ОС Windows, то поймете, что в ОС UNIX существует ряд прямых аналогий, перечисленных в табл. 1.1.
Таблица 1.1
| Элемент | UNIX | Windows |
|---|---|---|
| Объектный модуль | func.o | FUNC.OBJ |
| Статическая библиотека | lib.a | LIB.LIB |
| Программа | program | PROGRAM.EXE |
Совместно используемые библиотеки
У статических библиотек один недостаток - когда вы запускаете много приложений одновременно и все они используют функции из одной библиотеки, в результате образуется множество копий одних и тех же функций в памяти и множество реальных копий функций в самих файлах программ. Это может привести к потреблению большого объема полезной памяти и дискового пространства.
Устранить этот недостаток могут многие системы UNIX и Linux с поддержкой совместно используемых или разделяемых библиотек. Подробное обсуждение совместно используемых библиотек и их реализации в разных ОС не входило в нашу задачу, поэтому ограничимся только реализацией их в ОС Linux.
Совместно используемые библиотеки хранятся в тех же каталогах, что и статические, но у имен файлов совместно используемых библиотек другой суффикс. В типовой системе Linux имя совместно используемой версии стандартной библиотеки математических функций - /lib/libm.so.
Когда программа применяет совместно используемую библиотеку, она компонуется таким образом, что содержит не код функции как таковой, а ссылки на совместно используемый код, который станет доступен на этапе выполнения. Когда окончательная программа загружается в память для выполнения, ссылки на функции разрешаются, и выполняются вызовы совместно используемой библиотеки, которая будет загружаться в память по мере необходимости.
В этом случае система предоставляет возможность многим приложениям одновременно использовать единственную копию совместно используемой библиотеки и хранить ее на диске в единственном экземпляре. Дополнительным преимуществом служит возможность обновления совместно используемой библиотеки независимо от базирующихся на ней приложений. Применяются символические ссылки из файла /lib/libm.so на текущую версию библиотеки (/lib/libm.so.N, где N - основной номер версии - 6 во время написания книги). Когда ОС Linux запускает приложение, она учитывает номер версии библиотеки, требующийся приложению, чтобы не дать ведущим новым версиям библиотеки испортить более старые приложения.
Примечание
Вывод в следующем примере получен из дистрибутива SUSE 10.3. Если вы применяете другой дистрибутив, ваш вывод может слегка отличаться.
В системах Linux программа (динамический загрузчик), отвечающая за загрузку совместно используемых библиотек и разрешение ссылок на функции в клиентских программах, называется ld.so и может присутствовать в системе как ld-linux.so.2, или li-lsb.so.2, или li-lsb.so.3. Дополнительные каталоги поиска совместно используемых библиотек настраиваются в файле /etc/ld.so.conf, который после внесения изменений (например, если добавляются совместно используемые библиотеки X11 при установке графической оболочки X Window System) следует обработать командой ldconfig.
Запустив утилиту ldd, вы можете увидеть, какие совместно используемые библиотеки требуются программе. Например, если вы попытаетесь выполнить утилиту для приложения из нашего примера, то получите следующие строки:
$ ldd program
linux-gate.so.1 => (0xffffe000)
libc.so.6 => /lib/libc.so.6 (0xb7db4000)