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

Скомпоновав программу, можно ее запустить как обычно:

$ ./prog

Called mod1-x1

Called mod2-x2

41.3. Краткий обзор разделяемых библиотек

Когда программа компонуется со статической библиотекой (или вовсе без использования библиотек), итоговый исполняемый файл содержит копии всех объектных модулей, скомпонованных с программой. Таким образом, несколько разных программ могут содержать в себе копии одних и тех же объектных модулей. Подобная избыточность несет в себе несколько недостатков:

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

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

• если объектный модуль статической библиотеки требует каких-либо изменений (возможно, нужно закрыть дыру в безопасности или исправить ошибку), придется заново компоновать все исполняемые файлы, в которых этот модуль используется. Данный недостаток усугубляется тем фактом, что системному администратору необходимо знать, с какими приложениями скомпонована библиотека.

Для устранения представленных недочетов были придуманы разделяемые библиотеки. Их ключевая идея состоит в том, что одна копия объектного модуля разделяется между всеми программами, задействующими его. Объектные модули не копируются в компонуемый исполняемый файл; вместо этого единая копия библиотеки загружается в память при запуске первой программы, которой требуются ее объектные модули. Если позже будут запущены другие программы, использующие эту разделяемую библиотеку, они обращаются к копии, уже загруженной в память. Благодаря применению разделяемых библиотек исполняемые файлы требуют меньше места на диске и в виртуальной памяти (при выполнении).

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

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

• общий размер программ уменьшается, в связи с чем в некоторых случаях они могут быстрее загружаться в память, что ускоряет их запуск. Это относится только к большим разделяемым библиотекам, которые уже используются другими программами. На самом деле программа, первой загружающая разделяемую библиотеку, запускается дольше, поскольку данную библиотеку сначала нужно найти и загрузить в память;

• объектные модули не копируются в исполняемые файлы, а хранятся в единой разделяемой библиотеке, поэтому можно изменять общий код без необходимости выполнять повторную компоновку (ограничения описаны в разделе 41.8). Изменения можно вносить, даже когда запущенные программы уже применяют существующую версию разделяемой библиотеки.

Однако за эти дополнительные возможности приходится платить:

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

• разделяемые библиотеки должны быть скомпилированы с поддержкой адресно-независимого кода (см. подраздел 41.4.2), который ввиду применения дополнительных регистров (см. [Hubicka, 2003]) имеет издержки в большинстве архитектур;

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

Еще одно применение разделяемых библиотек заключается в построении на их основе интерфейсов JNI (Java Native Interface), которые позволяют коду, написанному на языке Java, напрямую задействовать возможности операционной системы; для этого достаточно вызывать функции из разделяемой библиотеки. Подробную информацию см. в [Liang, 1999] и [Rochkind, 2004].

41.4. Создание и использование разделяемых библиотек. Первые шаги

Для начала, чтобы понять, как устроены разделяемые библиотеки, рассмотрим минимальную цепочку операций, необходимых для их сборки и применения. Пока что намеренно проигнорируем общепринятую систему именования файлов разделяемых библиотек, описанную в разделе 41.6; она позволяет программам автоматически загружать самые свежие версии нужных им библиотек, а также делает возможным бесконфликтное сосуществование разных (так называемых мажорных) версий одной и той же библиотеки.

Перейти на страницу:

Похожие книги