Наши партнеры

UnixForum





Библиотека сайта rus-linux.net

На главную -> MyLDP -> Программирование и алгоритмические языки


Ulrich Drepper "Как писать разделяемые библиотеки"
Назад Оглавление Вперед

1.5.4 Область поиска

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

На самом деле область поиска состоит из трех частей. Основной частью является глобальная область поиска. Она первоначально состоит из самого исполняемого модуля и всех его зависимости. Зависимости добавляются к числу просматриваемых при первом расширенном проходе. Это означает, что сначала добавляются зависимости исполняемого модуля в том порядке, в каком их записи DT NEEDED следуют в динамической секции исполняемого модуля. Затем таким же самым образом добавляются зависимости первых зависимостей. Динамические объекты DSO, которые уже загружены, пропускаются; в списке они не появляются более одного раза. Процесс продолжается рекурсивно, и в какой-то момент он будет остановлен, поскольку есть только ограниченное число объектов DSO. Точное число объектов DSO, загружаемых таким образом, может варьироваться в широких пределах. Некоторые исполняемые модули зависят лишь от двух объектов DSO, другие - от 200.

Если в исполняемом модуле установлен флаг DF SYMBOLIC (смотрите раздел 2.2.7), то перед поиском в глобальной области добавляется объект с ссылкой. Обратите внимание, что предварительно добавляется только сам объект с ссылкой, а не его зависимости. Эффекты и причины этого будут рассмотрены позже.

Более сложные модификации области поиска возникают, когда объекты DSO загружаются динамически с использованием dlopen. Если объект DSO загружается динамически, он добавляет свой собственный набор зависимостей, которые, возможно, придется искать. Такие объекты, начиная с того, который был указан в вызове dlopen, добавляются в область поиска в случае, если среди тех объектов, которые были загружены dlopen, есть объект со ссылкой. Это означает, что такие объекты не добавляются в глобальную область поиска и они не участвуют в случае обычного поиска. Эта третья часть области поиска, которую мы будем называть локальной областью поиска объекта и которая, следовательно, будет зависеть от объекта, в котором была ссылка.

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

И, как правило, нет причины использовать RTLD GLOBAL. По причинам, которые будут объяснены позже, всегда очень советуют создавать зависимости со всеми объектами DSO, необходимыми для разрешения всех ссылок. RTLD GLOBAL часто используется, чтобы указать реализаций, которых нет во время компоновки объекта DSO. Поскольку этого следует избегать, нужно пользоваться этим флагом по минимуму. Даже если программисту придется прыгать через несколько обручей с тем, чтобы обойти проблемы, которые решаются с помощью RTLD GLOBAL, оно того стоит. Головная боль с отладкой и решением проблем, вызванных добавлением объектов в глобальную область поиска, гораздо больше.

С сентября 2004 года динамическому компоновщику из библиотеки GNU C известно еще одно расширение. Это расширение позволяет иметь дело с ситуацией, когда несколько определений символов с тем же именем оказываются несовместимыми и поэтому их нельзя вставлять и с ними работать. Как правило, это признак неверного проектирования со стороны тех, кто писал объекты DSO с противоречивыми определениями, а также неверного написания приложения, которое зависит от этих несовместимых разделяемых объектов. Мы предполагаем, что приложение app скомпоновано с DSO-объектом libone.so, в котором дублировано определение символа, и что динамически загружается DSO-объект libdynamic.so, который зависит от другого DSO-объекта libtwo.so, в котором также дублировано определение символа. Когда приложение запускается, то глобальный поиск может выполняться следующим образом:

app → libone.so → libdl.so → libc.so

Если теперь загружено libtwo.so, то дополнительная локальная область может быть примерно такой:

libdynamic.so → libtwo.so → libc.so

Поиск в этой локальной области выполняется после поиска в глобальной области, за исключением, возможно, libdynamic.so, поиск для которого в случае, если используется флаг DF DYNAMIC, выполняется сначала в том же самом объекте DSO. Но что произойдет, если в libdynamic.so потребуется дубликат символа? В конце концов мы до сих пор говорили, что результат всегда будет следующим: будет найдено определение в libone.so , поскольку поиск в libtwo.so выполняется только в локальной области видимости, что происходит после поиска в глобальной области. Если два определения несовместимы, то в программе возможны проблемы.

Это можно поменять в самой последней библиотеке GNU C с помощью установки значения ORing RTLD DEEPBIND в слове флага, передаваемого в dlopen в качестве второго параметра. Если это будет сделано, динамический компоновщик для всех объектов, которые были загружены с помощью обращения dlopen, будет выполнять поиск в локальной области раньше, чем в глобальной области. Для нашего примера это означает изменение порядка поиска для всех только что загруженных объектов DSO - libdynamic.so и libtwo.so, но не для libc.so, поскольку этот объект DSO уже был загружен. Для этих двух объектов DSO, для которых порядок поиска изменился, поиск ссылки на дубликат будет теперь происходить в libtwo.so. Для всех других объектов DSO поиск будет происходить в libone.so.

Хотя этот подход может показаться хорошим решением проблем совместимости, им следует пользоваться только в случае, если этого не удается избежать. На это есть несколько следующих причин:

  • Изменение в области поиска влияет на все символы и все объекты DSO, которые были загружены. Возможно, некоторые символы следовало бы вставлять с помощью определений в глобальной области, чего теперь не будет.
  • На уже загруженные объекты DSO влияние не оказывается, что ведет к несогласованным результатам, зависящим от того, загружен ли уже объект DSO (его можно загрузить динамически, следовательно, даже имеет место состояние гонки race condition).
  • LD PRELOAD неэффективен при поиске в динамически загружаемых объектах, поскольку предварительно загружаемые объекты, когда они добавляются сразу после исполняемого файла, являются частью области глобального поиска. Поэтому их поиск их будет происходить после поиска в локальной области видимости.
  • В приложениях может предполагаться, что локальные определения всегда предпочтительнее, чем другие определения. Этот пункт (и предыдущий) уже частично становится проблемой при использовании DF SYMBOLIC, но поскольку этот флаг не должен использоваться, аргументы все еще остаются допустимыми.
  • Если какие-либо неявно загружаемые объекты DSO будут загружены позже, то их область поиска будет изменена.
  • Наконец, флаг не является портируемым.

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


Предыдущий раздел:   Следующий раздел:
Назад Оглавление Вперед