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

UnixForum





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

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


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

1.5.6. Запуск конструкторов

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

Чтобы это гарантировать, динамический компоновщик должен выполнить топологическую сортировку списка объектов. Эта сортировка является нелинейным процессом. Точно также, как и для всех алгоритмов сортировки, его время выполнения будет равно по меньшей мере O(n log n), а поскольку это, на самом деле, топологическая сортировка, значение будет еще больше. Более того: поскольку порядок запуска не обязательно должен быть точно таким, как и порядок завершения (когда должны быть запущены процессы-финализаторы), весь процесс требуется повторить.

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

На этом этапе полезно посмотреть на то, как правильно писать конструкторы и деструкторы для объектов DSO. В некоторых системах есть соглашение, что в качестве конструктора и деструктора автоматически выбираются экспортируемые функции с именами init и fini, соответственно. Этому соглашению до сих пор еще следуют в GNU ld и использование функций с такими именами в системе Linux действительно станет причиной того, что эти функции будут использоваться для этих целей. Но это на 100% совершенно неверно!

С помощью этих функций программист переопределяет все, что система могла бы определить самостоятельно при инициализации и разрушении функциональности. Результатом является объект DSO, который инициализирован не полностью и это рано или поздно приводит к катастрофе. Правильным способом добавления конструкторов и деструкторов будет пометка функций атрибутами constructor и destructor, соответственно.

void
__attribute ((constructor))
init_function (void)
{
...
}
void
__attribute ((destructor))
fini_function (void)
{
...
}

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


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