Перейти к содержанию

8.4 Управление разделяемыми библиотеками (Managing Shared Libraries)

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

  • Как работают разделяемые библиотеки
  • Управление зависимостями разделяемых библиотек

Начнём с обсуждения принципов работы разделяемых библиотек.

Как работают разделяемые библиотеки (How Shared Libraries Work)

В Linux приложения, выполняющиеся в системе, могут совместно использовать элементы кода, называемые разделяемыми библиотеками (shared libraries). Это очень удобно: разделяемые библиотеки позволяют разработчикам не изобретать велосипед каждый раз, когда они пишут новую программу.

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

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

Совет

Разделяемые библиотеки в Linux работают во многом так же, как динамически подключаемые библиотеки (dynamic link libraries, DLL) в Windows.

Использование разделяемых библиотек даёт множество преимуществ. Очевидно, что это существенно ускоряет разработку. Кроме того, создаваемые программы становятся меньше и компактнее.

В Linux существует два типа разделяемых библиотек:

  • Динамические (Dynamic). Файлы динамических разделяемых библиотек хранятся в файловой системе Linux. Программисты просто вставляют в свой программный код ссылки на функции из этих разделяемых библиотек. Когда программа запускается, функции вызываются из динамических разделяемых библиотек, а не встраиваются в саму программу. В системе имеется конфигурационный файл со списком установленных динамических разделяемых библиотек и их расположения в файловой системе. Использование динамических разделяемых библиотек уменьшает общий размер исполняемого файла после компиляции. Однако они порождают проблему зависимостей: если программа вызывает функцию из динамической разделяемой библиотеки, которая не установлена в системе (или по какой-либо причине стала недоступна), приложение откажет в работе.

  • Статические (Static). В отличие от динамических, статические разделяемые библиотеки подключаются к программе статически при компиляции. По сути, при использовании статических библиотек фактический код вызываемых функций встраивается непосредственно в само приложение. Это, очевидно, увеличивает размер приложения. Зато приложение становится независимым от наличия разделяемых библиотек в системе, где оно выполняется, — в отличие от приложений, использующих динамические библиотеки.

Какой тип предпочтительнее? Это зависит от приложения. Большинство приложений, используемых в повседневной работе, применяют динамические разделяемые библиотеки. Это позволяет предоставить широкую функциональность при относительно небольшом объёме занимаемого места на диске. Тем не менее многие приложения используют статические библиотеки — в особенности те, которые предназначены для восстановления неисправно работающей системы. Вместо ссылок на динамические разделяемые библиотеки (которые в сценарии аварийного восстановления системы могут оказаться недоступны) такие приложения полностью самодостаточны и способны работать в минимальном окружении Linux.

Примечание

Хорошим примером приложения, использующего статические разделяемые библиотеки, служит автономная оболочка Stand-Alone Shell (sash). Она предназначена для восстановления неработающей системы Linux и включает множество встроенных функций. Благодаря этому, если раздел, где хранятся разделяемые библиотеки, недоступен, программа всё равно запустится и позволит выполнить необходимые действия по восстановлению системы.

Файлы разделяемых библиотек используют особый формат имени, позволяющий определить тип библиотеки. Этот синтаксис выглядит следующим образом:

libname.type.version

Обратите внимание, что имена всех разделяемых библиотек начинаются с lib, за которым следует имя разделяемой библиотеки. Часть имени type идентифицирует тип разделяемой библиотеки: символы so указывают, что файл является динамической разделяемой библиотекой, а символ a — что файл является статической библиотекой. Часть имени version задаёт номер версии библиотеки. Например, libfreetype.so.6.4.0 — это динамическая разделяемая библиотека.

Разобравшись с этим, перейдём к управлению зависимостями разделяемых библиотек.

Управление зависимостями разделяемых библиотек (Managing Shared Library Dependencies)

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

Конфигурационный файл динамических разделяемых библиотек — /etc/ld.so.conf. Пример его содержимого:

openSUSE:/etc # cat ld.so.conf
/usr/X11R6/lib64/Xaw3d
/usr/X11R6/lib64
/usr/lib64/Xaw3d
/usr/X11R6/lib/Xaw3d
/usr/X11R6/lib
/usr/lib/Xaw3d
/usr/x86_64-suse-linux/lib
/usr/local/lib
/opt/kde3/lib
/lib64
/lib
/usr/lib64
/usr/lib
/usr/local/lib64
/opt/kde3/lib64
include /etc/ld.so.conf.d/*.conf

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

Совет

Каталоги /lib/ и /usr/lib/ всегда считаются содержащими разделяемые библиотеки, поэтому они не указываются в файле /etc/ld.so.conf.

Чтобы просмотреть список всех разделяемых библиотек, доступных в системе Linux, введите ldconfig -p в командной строке. Пример вывода:

openSUSE:~ # ldconfig -p
1423 libs found in cache '/etc/ld.so.cache'
      libzypp.so.706 (libc6,x86-64) => /usr/lib64/libzypp.so.706
      libzio.so.0 (libc6,x86-64) => /usr/lib64/libzio.so.0
      libz.so.1 (libc6,x86-64) => /lib64/libz.so.1
      libz.so.1 (libc6) => /lib/libz.so.1
      libz.so (libc6,x86-64) => /usr/lib64/libz.so
      liby2util.so.4 (libc6,x86-64) => /usr/lib64/liby2util.so.4
      liby2.so.2 (libc6,x86-64) => /usr/lib64/liby2.so.2
      libyui.so.3 (libc6,x86-64) => /usr/lib64/libyui.so.3
      libycpvalues.so.3 (libc6,x86-64) => /usr/lib64/libycpvalues.so.3
...

Примечание

Если вы не вошли в систему как root, необходимо указывать полный путь к ldconfig в команде (/sbin/ldconfig).

Вы также можете просмотреть разделяемые библиотеки, необходимые конкретному приложению, с помощью команды ldd. Синтаксис: ldd -v имя_исполняемого_файла. Например, чтобы узнать, какие разделяемые библиотеки требуются команде ip (используемой для управления сетевыми соединениями), введите ldd -v /sbin/ip в командной строке.

Примечание

При использовании команды ldd необходимо указывать полный путь к исполняемому файлу вместе с его именем.

Пример вывода:

openSUSE:~ # ldd -v /sbin/ip
      linux-vdso.so.1 => (0x00007fffe4ab2000)
      libdl.so.2 => /lib64/libdl.so.2 (0x00007f7c07e7d000)
      libc.so.6 => /lib64/libc.so.6 (0x00007f7c07b1d000)
      /lib64/ld-linux-x86-64.so.2 (0x00007f7c08081000)

      Version information:
      /sbin/ip:
          libdl.so.2 (GLIBC_2.2.5) => /lib64/libdl.so.2
          libc.so.6 (GLIBC_2.7) => /lib64/libc.so.6
          libc.so.6 (GLIBC_2.4) => /lib64/libc.so.6
          libc.so.6 (GLIBC_2.3.4) => /lib64/libc.so.6
          libc.so.6 (GLIBC_2.2.5) => /lib64/libc.so.6
      /lib64/libdl.so.2:
          ld-linux-x86-64.so.2 (GLIBC_PRIVATE) => /lib64/ld-linux-x86-64.so.2
          libc.so.6 (GLIBC_PRIVATE) => /lib64/libc.so.6
          libc.so.6 (GLIBC_2.2.5) => /lib64/libc.so.6
      /lib64/libc.so.6:
          ld-linux-x86-64.so.2 (GLIBC_PRIVATE) => /lib64/ld-linux-x86-64.so.2
          ld-linux-x86-64.so.2 (GLIBC_2.3) => /lib64/ld-linux-x86-64.so.2

Одно из ключевых применений команды ldd — проверка зависимостей разделяемых библиотек. Она позволяет определить, установлены ли все библиотеки, необходимые данному приложению. Если все библиотеки присутствуют, сообщений об ошибках не будет, как в приведённом выше примере. Если какой-либо файл библиотеки отсутствует, будет выведено сообщение об ошибке. После этого вы можете найти и установить отсутствующую библиотеку, и всё заработает.

Итак, как приложение узнаёт, в каких каталогах искать разделяемую библиотеку? Приложения не обращаются непосредственно к файлу /etc/ld.so.conf. Вместо этого они проверяют кеш библиотек и переменную окружения LD_LIBRARY_PATH. Кеш библиотек — это файл /etc/ld.so.cache. Он содержит список всех системных библиотек и обновляется при первоначальной загрузке системы.

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

  • Использовать ldconfig. Команда ldconfig служит для ручного перестроения кеша библиотек.
  • Задать LD_LIBRARY_PATH. Можно также добавить путь в переменную окружения LD_LIBRARY_PATH. Как правило, следует добавить новый путь в конец списка каталогов, которые уже могут содержаться в переменной, поэтому используйте следующие команды:
set LD_LIBRARY_PATH=$LD_LIBRARY_PATH;new_path
export LD_LIBRARY_PATH

Как правило, первый вариант предпочтительнее. Он гарантирует, что разделяемые библиотеки всегда будут доступны приложениям, которые в них нуждаются, даже после перезагрузки системы. Значение LD_LIBRARY_PATH обычно задаётся лишь в ситуациях, когда в разных каталогах установлены две версии одной и той же разделяемой библиотеки и нужно использовать одну из них в приоритете над другой.

Упражнение 8-6. Работа с разделяемыми библиотеками

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

VIDEO Посмотрите видеозапись упражнения 8-6, в которой показано, как выполнить это задание.

Выполните следующие шаги:

  1. Запустив систему, откройте сеанс терминала.
  2. При необходимости переключитесь на учётную запись суперпользователя root, введя su -, а затем пароль пользователя root.
  3. Просмотрите разделяемые библиотеки, используемые исполняемым файлом ping, введя ldd -v /bin/ping в командной строке. Вы должны увидеть, что ping требует разделяемую библиотеку libc.so.6.
  4. Найдите расположение файла библиотеки lib64/libc.so.6 в системе, введя find / -name libc.so.6 в командной строке. На 32-разрядной системе файл находится в каталоге /lib, на 64-разрядной — скорее всего в /lib64.
  5. Просмотрите кеш библиотек системы, введя ldconfig -p в командной строке.
  6. Перестройте кеш библиотек, введя ldconfig -v в командной строке.

На этом данная глава завершается! Теперь такие понятия, как apt, tarball и rpm, не должны вас пугать — вы знаете, как эффективно управлять программным обеспечением в системе Linux!