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

Библиотеки используются в основном, чтобы избежать переписывания кода (или копирования и вставки). Мы сэкономим время на компиляцию каждый раз, когда код уже скомпилирован. Кроме того, мы уже знаем, что при выполнении программы, которую мы тестируем и исправляем, нам приходится компилировать много-много раз.
Уже скомпилированный код будет протестирован и надежен, чтобы его можно было использовать столько раз как надо.

Чтобы наш код работал в библиотеке, нам нужно организовать его следующим образом:

  • Один или несколько исходных файлов .c с кодом наших функций.
  • Один или несколько заголовочных файлов .h с типами (typedefs, structs и enums) и прототипами функций, которые мы хотим использовать.
holberton.h
#ifndef _LIBRARIES_1_H
#define _LIBRARIES_1_H
int add(int a, int b);
int sub(int a, int b);
#endif

В другом файле создаем исходники:

libraries.c
int add(int a, int b)
{
    return a + b;
}
int sub(int a, int b)
{
   return a-b;
}

Это файл с парой простых функций сложения () и вычитания ().

В Linux мы можем создавать два типа библиотек: статические и динамические.

Статическая библиотека — это библиотека, которая «копирует» в нашу программу при ее компиляции. Когда у нас есть исполняемый файл нашей программы, библиотека становится бесполезной (она пригодится для других будущих проектов). Мы могли бы его удалить и наша программа продолжала бы работать, так как в ней есть копия всего, что нужно. Копируется только та часть библиотеки, которая необходима.

Например, если в библиотеке есть две функции, а наша программа вызывает только одну, копируется только эта функция.

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

Одним из самых известных различий между статическими и динамическими библиотеками является

  • Если мы изменим статическую библиотеку, это не повлияет на исполняемые файлы, а если мы изменим динамическую, это повлияет на исполняемые файлы.

Преимущества и недостатки каждого из этих типов библиотек:

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

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

Есть файлы fich1.c и fich2.c, содержащие код функций, которые мы хотим включить в библиотеке libfich.a. Чтобы получить объектные файлы, мы компилируем так:

$ gcc -c -o fich1.o fich1.c
$ gcc -c -o fich2.o fich2.c

Затем мы упаковываем файлы, полученные с помощью ar.

$ ar rcs libfich.a fich1.o fich2.o

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

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

Если мы написали программу apli1.c, которая использует библиотеку, созданную в предыдущем пункте, и мы хотим ее скомпилировать, мы должны сделать следующее:

  1. Скомпилируйте программу:
$ gcc -c -o apli1.o apli1.c

2. Программа загружается с библиотекой с указанием где она находится и как ее зовут:

$ gcc -o apli1 apli1.o -Ldir_lib -lfich

Соображения:

  • В примере предполагается, что библиотека и файл интерфейса находятся в каталоге с именем dir_lib.
  • Параметр -I используется для указания расположения файлов интерфейса.
  • Параметр -L используется для указания каталога, в котором находится библиотека.
  • Опция -l предназначена для указания имен библиотек, которые будут использоваться, но ни префикс lib, ни расширение .a не должны быть написаны, так как компилятор ожидает вышеупомянутого именования. правила, которым необходимо следовать.

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

При создании динамического объекта необходимо, чтобы указанный код объекта не зависел от позиции, для получения этого типа кода используется опция -fPIC (Position Independent Code) должен быть указан для компилятора. Этот флаг необходимо указывать как при компиляции, так и при сборке библиотеки.

$ gcc -fPIC -c -o fich1.o fich1.c
$ gcc -fPIC -c -o fich2.o fich2.c

Для монтирования объектов также необходимо указать параметр -shared, чтобы в результате был объектный файл ‘shareable’.

$ gcc -shared -fPIC -o libfich.so fich1.o fich2.o

В этом случае библиотека имеет расширение .so, что означает общий объект.

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

Чтобы использовать эту библиотеку из программы, вам не нужно делать ничего дополнительно; Точно так же, как и в случае со статической библиотекой.

При использовании библиотеки компилятор сначала ищет динамическую версию (.so), если не находит, то ищет статическую версию. Если доступны обе версии библиотеки и должна использоваться статическая версия, ассемблеру необходимо указать флаг -static.

Когда программа использует динамические библиотеки, системе необходимо найти их во время выполнения (в отличие от статических библиотек). Места, где программа ищет динамические библиотеки, следующие (в этом порядке):

  • В каталогах переменная LD_LIBRARY_PATH.
  • В файле ld.so.cache.
  • В каталогах /usr/lib и /lib.
  • В каталогах, содержащихся в файле ld.so.conf.

Пример

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

#include <stdio.h>
void rehola(int i)
{
   printf("Hola mundo por %d vez.\n", i);
}

И у нас есть файл со следующим кодом под названием apli3.c:

#include <stdio.h>
#include <dlfcn.h>
#define RUTA_LIBHOLA "/home/user/libhola.so"
int main(){
   int i = 3;
   void *pLibHola;                       
   void (*pFuncion)(int);                
   if ((pLibHola = dlopen(RUTA_LIBHOLA, RTLD_LAZY)) ==NULL) {
      fprintf(stderr, dlerror());
      return 1;
   }
   if ((pFuncion = dlsym(pLibHola, "rehola")) == NULL) {
      fprintf(stderr, dlerror());
      return 1;
   }
   if ((pFuncion = dlsym(pLibHola, "rehola")) == NULL) {
      fprintf(stderr, dlerror());
      return 1;
   }
   pFuncion(i);
   dlclose(pLibHola);
   return(0);
}

Этот код просто компилируется с помощью:

$ gcc -o apli3 apli3.c -ldl

Параметр -ldl необходим для возможности использования библиотеки libdl.so, отвечающей за реализацию динамических ссылок, но ни в коем случае были сделаны ссылки на библиотеку libhola, только там, где она находится.

Если мы запустим программу, мы увидим следующее:

$ ./apli3
   Hola mundo por 3 vez.