Как обойти кеширование dlopen()?
Согласно его справочной странице, dlopen()
не будет загружать одну и ту же библиотеку дважды:
Если тот же общий объект загружается снова с помощью dlopen(), возвращается тот же дескриптор объекта. Динамический компоновщик поддерживает количество ссылок для дескрипторов объектов, поэтому динамически загруженный общий объект не освобождается до тех пор, пока dlclose() не будет вызвана для него столько раз, сколько для него выполнено dlopen (). Любые возвраты инициализации (см. Ниже) вызываются только один раз. Однако последующий вызов dlopen(), который загружает тот же общий объект с RTLD_NOW, может вызвать разрешение символов для общего объекта, ранее загруженного с RTLD_LAZY.
(акцент мой).
Но что на самом деле определяет идентичность общих объектов? Я пытался заглянуть в код, но не зашел очень далеко. Это:
- некоторая форма нормализованного имени пути (например, realpath?)
- инод?
- содержимое библиотеки?
Я почти уверен, что могу исключить этот последний пункт, поскольку фактическая копия файловой системы дает два разных дескриптора.
Чтобы объяснить мотивацию этого вопроса: я работаю с кодом, который имеет статические глобальные переменные. Мне нужно несколько экземпляров этого кода, чтобы запустить потокобезопасным способом. Мой текущий подход состоит в том, чтобы скомпилировать и связать указанный код в динамическую библиотеку и загрузить эту библиотеку несколько раз. С некоторой магией компоновщика создается несколько копий глобалов и разрешается доступ в каждой библиотеке к ее собственным копиям. Единственная проблема заключается в том, что мой прототип копирует сгенерированную библиотеку n раз для n одновременных использований. Это не только несколько уродливо, но я также подозреваю, что он может сломаться на другой платформе.
Так каково точное поведение dlopen()
в соответствии со стандартом POSIX?
редактировать: потому что это появилось в комментарии и ответе, никакой рефакторинг кода, безусловно, не вариант. Это может занять месяцы или даже годы работы и потенциально пожертвовать всеми преимуществами использования кода в первую очередь. Существует постоянный исследовательский проект, который мог бы решить эту проблему гораздо чище, но это настоящее исследование, которое может провалиться. Мне нужно решение сейчас.
edit2: потому что люди все еще, кажется, не верят, что сценарий использования действительно действителен. Я работаю над чисто функциональным языком, который должен быть встроен в более крупное приложение C/C++. Поскольку мне нужен прототип с сборщиком мусора, проверенным проверщиком типов и приемлемой производительностью как можно скорее, я использовал OCaml в качестве промежуточного кода. Прямо сейчас я собираю исходный модуль в модуль OCaml, связываю сгенерированный объектный код (включая запуск и т. Д.) В разделяемую библиотеку со средой выполнения OCaml и dlopen(), которая разделяет библиотеку. Каждый.so имеет свою собственную копию среды выполнения, в том числе несколько глобальных переменных (например, указатель на молодое поколение), и это, или, скорее, должно быть совершенно нормально. Библиотека предоставляет ровно две функции: инициализатор и один экспорт, который выполняет то, для чего предназначен исходный модуль. Никакие символы среды выполнения OCaml не экспортируются и не передаются. Когда я загружаю библиотеку, ее внутренние символы перемещаются, как и ожидалось, единственная проблема, с которой я сталкиваюсь сейчас, заключается в том, что мне действительно нужно копировать файл.so для каждого экземпляра задания во время выполнения.
Относительно локального хранилища потоков: на самом деле это интересная идея, поскольку модификация среды выполнения действительно довольно проста. Но проблема в том, что машинный код генерируется компилятором OCaml, так как он не может выдавать инструкции загрузки для символов tls (пока?).
1 ответ
POSIX говорит:
Только одна копия объектного файла заносится в адресное пространство, даже если dlopen() вызывается несколько раз при обращении к файлу, и даже если для ссылки на файл используются разные пути.
Так что ответ "инод". Копирование файла библиотеки "должно работать", но жесткие ссылки не будут. Кроме. Так как они будут выставлять одни и те же глобальные символы, и когда это произойдет, все (переносимость) ставки отключены. Вы находитесь в середине слабо определенного поведения, которое возникло благодаря исправлению ошибок, а не в хорошем дизайне.
Не копайте глубже, когда вы в яме. Подход к добавлению дополнительных ужасных хаков для создания принципиально сломанной библиотеки просто приводит к дополнительным поломкам. Просто потратьте несколько часов, чтобы исправить библиотеку, чтобы она не использовала глобальные переменные, вместо того, чтобы потратить несколько дней, чтобы взломать динамические ссылки (которые в лучшем случае будут непереносимы).