При работе над большими проектами компилировать множество файлов вместе может быть неприятно. Это еще большая проблема при обмене файлами с другими. Библиотеки позволяют нам организовывать и использовать функции без необходимости копировать исходный код в каждое место.

Если вы когда-нибудь программировали на C, вы, вероятно, начали свой файл с чего-то вроде

include <stdlib.h>

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

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

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

Создание библиотек

Чтобы создать библиотеку, сначала создайте заголовочный файл, содержащий все прототипы, которые вы собираетесь включить. Затем вам нужно скомпилировать код.

$ gcc -c *.c

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

Создание статических библиотек

Для создания статической библиотеки используйте команду архиватора:

$ ar rc libfoo.a *.o

Флаг r предписывает архиватору заменить старые версии файлов новыми, а флаг c предписывает создать новый архив, если он еще не существует. Все статические библиотеки заканчиваются на .a для archive.

Статические библиотеки должны быть проиндексированы после создания. Это упорядочивает символы библиотеки, упрощая и ускоряя доступ к ним.

$ ranlib newlibrary.a

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

$ gcc -shared -o libfoo.so *.o

Флаг -shared создает общую библиотеку. Все динамические библиотеки заканчиваются на .soдля общего объекта.

Давайте воспользуемся командой ldd для вывода зависимостей наших общих объектов.

myvm:~/library_practice $ gcc -L. main.c libholberton.so -o check
myvm:~/library_practice$ ldd check
linux-vdso.so.1 =>  (0x00007fff27372000)
libfoo.so => not found
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 => (0x00007fc349e7c000)
/lib64/ld-linux-x86-64.so.2 (0x00007fc34a245000)

Наконец, экспортируйте путь к библиотеке, чтобы дочерние процессы могли получить к нему доступ с помощью переменной среды.

Подробнее о дочерних процессах читайте в этой статье, в которой я разбираю оболочку.

$ export LD_LIBRARY_PATH=$PWD:$LD_LIBRARY_PATH

Когда мы снова проверим это с помощью ldd, мы увидим, что общие зависимости были обновлены.

myvm:~/library_practice $ gcc -L. main.c libholberton.so -o check
myvm:~/library_practice$ ldd check
linux-vdso.so.1 =>  (0x00007fff27372000)
libfoo.so => /home/vagrant/library_practice/libfoo.so (0x00007fd7b2e79000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 => (0x00007fc349e7c000)
/lib64/ld-linux-x86-64.so.2 (0x00007fc34a245000)

После создания вы можете использовать команду nm для отображения списка символов в вашей библиотеке.

myvm:~/library_practice $ nm liball.so
0000000000000f89 T _abs
0000000000000af7 T _atoi
0000000000202040 B __bss_start
0000000000202040 b completed.6982
w __cxa_finalize@@GLIBC_2.2.5
0000000000000900 t deregister_tm_clones
0000000000000970 t __do_global_dtors_aux
0000000000201e08 t __do_global_dtors_aux_fini_array_entry
0000000000202038 d __dso_handle
0000000000201e18 d _DYNAMIC
0000000000202040 D _edata
0000000000202048 B _end
000000000000103c T _fini
00000000000009b0 t frame_dummy
0000000000201e00 t __frame_dummy_init_array_entry
0000000000001398 r __FRAME_END__
0000000000202000 d _GLOBAL_OFFSET_TABLE_
w __gmon_start__
0000000000000890 T _init
0000000000000de6 T _isalpha
0000000000000b01 T _isdigit
0000000000000c81 T _islower
00000000000009e5 T _isupper
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
0000000000201e10 d __JCR_END__
0000000000201e10 d __JCR_LIST__
w _Jv_RegisterClasses
0000000000000b22 T _memcpy
0000000000000a06 T _memset
0000000000001016 T _putchar
0000000000000ca7 T _puts
0000000000000930 t register_tm_clones
0000000000000a41 T _strcat
0000000000000b65 T _strchr
0000000000000ce2 T _strcmp
0000000000000faf T _strcpy
0000000000000baf T _strlen
0000000000000bd9 T _strncpy
0000000000000e1a T _strpbrk
0000000000000d57 T _strspn
0000000000000ee6 T _strstr
0000000000202040 d __TMC_END__
U write@@GLIBC_2.2.5

Использование библиотек в системе Linux

По словам Артура, «развлекаться несложно, когда у тебя есть читательский билет!»

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

Использование статической библиотеки

Статические библиотеки компилируются вместе с файлами, необходимыми для программы.

$ gcc main.c -L. -lfoo.a -o executablename

При работе со статическими библиотеками параметр -L. флаг сообщает компилятору, где искать библиотеку.

Использование динамической библиотеки

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

$ gcc main.c -lfoo -o executablename

Поскольку мы уже обновили путь к библиотеке, нам не нужно ничего указывать с помощью -L.

Но не забывайте, что динамические библиотеки также называются разделяемыми библиотеками. Так что же произойдет, если мы захотим, чтобы другие люди могли их использовать? Во-первых, поместите библиотеку в общую зону и обновите разрешения. Затем используйте команду ldconfig, чтобы обновить кеш и создать ссылку на общую библиотеку.

Как только вы освоите эти шаги, вы будете готовы создавать свои собственные библиотеки. Украсьте закладку временем, которое вы сэкономите на своем следующем проекте!