Библиотека сайта rus-linux.net
На главную -> MyLDP -> Программирование и алгоритмические языки
Ulrich Drepper "Как писать разделяемые библиотеки" | ||
Назад | Оглавление | Вперед |
1.5. Использование динамического компоновщика
Вторая фаза запуска программы происходит в динамическом компоновщике. В его задачи входит:
- определение и загрузка зависимостей;
- перемещение (relocate) приложения и всех зависимостей;
- инициализация приложения и зависимостей в правильном порядке.
Далее мы более подробно рассмотрим только ту обработку, которая связана с перемещением. Для двух других пунктов пути повышения производительности ясны: использовать меньше зависимостей. Каждый используемый объект инициализируется только один раз, но потребуется выполнить определенную топологическую сортировку. Затраты, возникающие при идентификации и загрузке, также зависят от числа зависимостей; в большинстве (или во всех?) реализациях масштаб такой зависимости будет нелинейным.
Процесс перемещения обычно {Примечание 3} является самой ресурсоемкой частью работы динамического компоновщика. Это процесс, который является, по меньшей мере, асимптотическим O(R + nr) , где R - количество относительных перемещений, г - количеством именованных перемещений, а n — количество участвующих в перемещении динамических объектов DSO (плюс один основной исполняемый модуль). Отсутствие информации в функции хэш-таблицы ELF и различные расширения ELF, изменяющие функцию поиска символов, могут увеличить этот показатель до O(R + rn log s), где s - число символов. Должно быть ясно, что для повышения производительности важно максимально уменьшить количество перемещений и количество символов. После того, как мы объясним процесса перемещения, мы приведем некоторые оценочные значения для конкретных чисел.
Примечание 3: Здесь мы игнорируем поддержку предварительной компоновки, которая во многих случаях может значительно снизить или даже ликвидировать затраты на перемещение.
1.5.1. Процесс перемещения
Перемещение в данном контексте означает настройку приложения и динамических объектов DSO, загружаемых в качестве зависимостей, в соответствие со своими собственными, а также другими адресами загрузки. Есть два вида зависимостей:
- Зависимости от местоположения, которые, как известно, должны быть в самом объекте. Они не связаны с конкретным символом, поскольку компоновщик знает о взаимном расположении частей в объекте. Обратите внимание, что поскольку во время компоновки адрес загрузки кода известен, в приложении нет относительных перемещений и, следовательно, перемещение может выполнить статический компоновщик.
- Символьные зависимости. В общем случае, но не обязательно, ссылка на определение, которое находится в другом объекте.
Реализация относительного перемещения проста. Компоновщик на этапе компоновки может вычислить смещение для того места, куда в файле объекта требуется произвести перемещение. К этому значению динамический компоновщик должен только добавить адрес загрузки объекта и сохранить результат в месте, указываемом для перемещения. На этапе выполнения динамический компоновщик должен потратить только чуть-чуть времени, которое всегда будет одним и тем же (константным) и которое не будет увеличиваться в случае, если будет использовано больше объектов DSO.
Перемещение, связанное с символом, существенно более сложное. Процесс, с помощью которого происходит разрешение символов ELF, был сделан очень мощным с тем, чтобы с его помощью можно было решать различные проблемы. Однако, вся эта мощная функциональность приводит к увеличению сложности и повышает затраты на этапе выполнения программы. Читатели, следящие за описанием, могут поинтересоваться решениями, приведшими к созданию такого процесса. Здесь мы не сможем рассмотреть этот вопрос; читатели могут обратиться к обсуждению спецификации ELF. Факт в том, что перемещение, связанно с символом, является затратным процессом и чем больше используется объектов DSO или чем больше в объектах DSO определено символов, тем дольше будет происходить поиск символов.
Результат любого перемещения будет храниться где-нибудь в объекте вместе с ссылкой. Обычно идеальным для этого местом является сегмент данных. Если код создается пользователем, компилятором или компоновщиком неправильно, то при перемещении могут модифицироваться текстовые сегменты или сегменты, доступные только для чтения. Если объект, как это требуется в спецификация ELF, помечается значением DF TEXTREL, указываемым в записи DT FLAGS динамической секции (или если в двоичных модулях старого вида будет указан флаг DT TEXTREL), то динамический компоновщик обработает эту ситуацию правильно. Но в результате к модифицированной странице не смогут обращаться другие процессы, использующие тот же самый объект. Сам процесс изменения также довольно медленный, поскольку ядро должно еще совсем немного скорректировать структуры данных, используемых при обработке памяти.
Предыдущий раздел: | Следующий раздел: | |
Назад | Оглавление | Вперед |