Jump to content

Динамическая загрузка

Динамическая загрузка — это механизм, с помощью которого компьютерная программа может во время выполнения загружать библиотеку (или другой двоичный файл ) в память, извлекать адреса функций и переменных, содержащихся в библиотеке, выполнять эти функции или получать доступ к этим переменным и выгружать библиотека по памяти. Это один из трех механизмов, с помощью которых компьютерная программа может использовать другое программное обеспечение внутри программы; остальные — статическое связывание и динамическое связывание . В отличие от статического и динамического связывания, динамическая загрузка позволяет компьютерной программе запускаться при отсутствии этих библиотек, обнаруживать доступные библиотеки и потенциально получать дополнительные функциональные возможности. [1] [2]

Динамическая загрузка была обычным методом для операционных систем IBM для System/360 , таких как OS/360 , особенно для ввода-вывода подпрограмм , а также для COBOL и PL/I библиотек времени выполнения , и продолжает использоваться в операционных системах IBM для z/Architecture. , например z/OS . Что касается прикладного программиста, загрузка в значительной степени прозрачна, поскольку она в основном обрабатывается операционной системой (или ее подсистемой ввода-вывода). Основные преимущества:

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

IBM Стратегическая обработки транзакций система , CICS (начиная с 1970-х годов), широко использует динамическую загрузку как для своего ядра , так и для обычной загрузки прикладных программ . Исправления в прикладных программах можно вносить в автономном режиме, а новые копии измененных программ загружаться динамически без необходимости перезапуска CICS. [3] [4] (который может работать и часто работает 24 часа в сутки, 7 дней в неделю ).

Общие библиотеки были добавлены в Unix в 1980-х годах, но изначально без возможности позволить программе загружать дополнительные библиотеки после запуска. [5]

Использование

[ редактировать ]

Динамическая загрузка чаще всего используется при реализации программных плагинов . [1] Например, веб-сервер Apache *.dso Файлы плагина «динамический общий объект» — это библиотеки , которые загружаются во время выполнения с помощью динамической загрузки. [6] Динамическая загрузка также используется при реализации компьютерных программ , где несколько различных библиотек могут предоставлять необходимые функциональные возможности и где пользователь имеет возможность выбрать, какую библиотеку или библиотеки предоставить.

Не все системы поддерживают динамическую загрузку. Unix-подобные операционные системы, такие как macOS , Linux и Solaris, обеспечивают динамическую загрузку с помощью библиотеки языка программирования C «dl». Операционная Windows система обеспечивает динамическую загрузку через Windows API .

Краткое содержание

[ редактировать ]
Имя Стандартный API POSIX/Unix API Microsoft Windows
Включение заголовочного файла #include <dlfcn.h> #include <windows.h>
Определения заголовка dl

( libdl.so, libdl.dylibи т. д. в зависимости от ОС )

kernel32.dll
Загрузка библиотеки dlopen LoadLibrary
LoadLibraryEx
Извлечение содержимого dlsym GetProcAddress
Выгрузка библиотеки dlclose FreeLibrary

Загрузка библиотеки

[ редактировать ]

Загрузка библиотеки осуществляется с помощью LoadLibrary или LoadLibraryEx в Windows и с dlopen в Unix-подобных операционных системах . Ниже приведены примеры:

Большинство Unix-подобных операционных систем (Solaris, Linux, *BSD и т. д.)

[ редактировать ]
void* sdl_library = dlopen("libSDL.so", RTLD_LAZY);
if (sdl_library == NULL) {
   // report error ...
} else {
   // use the result in a call to dlsym
}

В качестве библиотеки Unix :

void* sdl_library = dlopen("libSDL.dylib", RTLD_LAZY);
if (sdl_library == NULL) {
   // report error ...
} else {
   // use the result in a call to dlsym
}

В качестве платформы macOS :

void* sdl_library = dlopen("/Library/Frameworks/SDL.framework/SDL", RTLD_LAZY);
if (sdl_library == NULL) {
   // report error ...
} else {
   // use the result in a call to dlsym
}

Или, если фреймворк или пакет содержит код Objective-C:

NSBundle *bundle = [NSBundle bundleWithPath:@"/Library/Plugins/Plugin.bundle"];
NSError *err = nil;
if ([bundle loadAndReturnError:&err])
{
    // Use the classes and functions in the bundle.
}
else
{
    // Handle error.
}
HMODULE sdl_library = LoadLibrary(TEXT("SDL.dll"));
if (sdl_library == NULL) {
   // report error ...
} else {
   // use the result in a call to GetProcAddress
}

Извлечение содержимого библиотеки

[ редактировать ]

Извлечение содержимого динамически загружаемой библиотеки достигается с помощью GetProcAddress в Windows и с dlsym в Unix -подобных операционных системах .

Unix-подобные операционные системы (Solaris, Linux, *BSD, macOS и т. д.)

[ редактировать ]
void* initializer = dlsym(sdl_library, "SDL_Init");
if (initializer == NULL) {
   // report error ...
} else {
   // cast initializer to its proper type and use
}

В macOS при использовании пакетов Objective-C также можно:

Class rootClass = [bundle principalClass]; // Alternatively, NSClassFromString() can be used to obtain a class by name.
if (rootClass)
{
    id object = [[rootClass alloc] init]; // Use the object.
}
else
{
    // Report error.
}
FARPROC initializer = GetProcAddress(sdl_library,"SDL_Init");
if (initializer == NULL) {
   // report error ...
} else {
   // cast initializer to its proper type and use
}

Преобразование указателя на библиотечную функцию

[ редактировать ]

Результат dlsym() или GetProcAddress() должен быть преобразован в указатель соответствующего типа, прежде чем его можно будет использовать.

В Windows преобразование простое, поскольку FARPROC по сути уже является указателем на функцию :

typedef INT_PTR (*FARPROC)(void);

Это может быть проблематично, если необходимо получить адрес объекта, а не функции. Однако обычно все равно требуется извлечь функции, так что обычно это не проблема.

typedef void (*sdl_init_function_type)(void);
sdl_init_function_type init_func = (sdl_init_function_type) initializer;

Юникс (POSIX)

[ редактировать ]

Согласно спецификации POSIX, результат dlsym() это void указатель. Однако указатель функции не обязательно должен иметь тот же размер, что и указатель объекта данных, и, следовательно, допустимое преобразование между типами void* а указатель на функцию может оказаться непросто реализовать на всех платформах.

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

typedef void (*sdl_init_function_type)(void);
sdl_init_function_type init_func = (sdl_init_function_type)initializer;

Приведенный выше фрагмент выдает предупреждение о некоторых компиляторах: warning: dereferencing type-punned pointer will break strict-aliasing rules. Еще один обходной путь:

typedef void (*sdl_init_function_type)(void);
union { sdl_init_function_type func; void * obj; } alias;
alias.obj = initializer;
sdl_init_function_type init_func = alias.func;

который отключает предупреждение, даже если действует строгое псевдонимирование. При этом используется тот факт, что чтение из члена объединения, отличного от того, в который была записана последняя запись (так называемое « каламбур типов »), является обычным явлением и явно разрешено, даже если действует строгое псевдонимирование, при условии, что доступ к памяти осуществляется через тип объединения. напрямую. [7] Однако в данном случае это не совсем так, поскольку указатель на функцию копируется для использования вне объединения. Обратите внимание, что этот трюк может не работать на платформах, где размер указателей данных и размер указателей функций не совпадают.

Решение проблемы указателя функции в системах POSIX

[ редактировать ]

Факт остается фактом: любое преобразование между указателями на функцию и объект данных следует рассматривать как (по своей сути непереносимое) расширение реализации, и что не существует «правильного» способа прямого преобразования, поскольку в этом отношении стандарты POSIX и ISO противоречат друг другу. друг друга.

Из-за этой проблемы документация POSIX по dlsym() в устаревшем выпуске 6 говорилось, что «в будущей версии может быть добавлена ​​новая функция для возврата указателей на функции, либо текущий интерфейс может быть признан устаревшим в пользу двух новых функций: одна, которая возвращает указатели на данные, и другая, которая возвращает указатели на функции». [8]

В последующей версии стандарта (выпуск 7, 2008 г.) проблема обсуждалась, и был сделан вывод, что указатели функций должны быть конвертированы в void* для соответствия POSIX. [8] Это требует от производителей компиляторов реализовать рабочий состав для этого случая.

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

Выгрузка библиотеки

[ редактировать ]

Загрузка библиотеки приводит к выделению памяти; библиотека должна быть освобождена во избежание утечки памяти . Кроме того, невозможность выгрузить библиотеку может помешать файловой системы операциям с файлом , содержащим библиотеку. Выгрузка библиотеки осуществляется с помощью FreeLibrary в Windows и с dlclose в Unix-подобных операционных системах . Однако выгрузка DLL может привести к сбою программы, если объекты в основном приложении ссылаются на память, выделенную внутри DLL. Например, если DLL вводит новый класс и DLL закрывается, дальнейшие операции с экземплярами этого класса из основного приложения, скорее всего, приведут к нарушению доступа к памяти. Аналогично, если DLL вводит фабричную функцию для создания экземпляров динамически загружаемых классов, вызов или разыменование этой функции после закрытия DLL приводит к неопределенному поведению.

Unix-подобные операционные системы (Solaris, Linux, *BSD, macOS и т. д.)

[ редактировать ]
dlclose(sdl_library);
FreeLibrary(sdl_library);

Специальная библиотека

[ редактировать ]

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

Unix-подобные операционные системы позволяют программистам получать доступ к глобальной таблице символов, которая включает в себя как основной исполняемый файл, так и загружаемые впоследствии динамические библиотеки.

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

Unix-подобные операционные системы (Solaris, Linux, *BSD, macOS и т. д.)

[ редактировать ]
void* this_process = dlopen(NULL,0);
HMODULE this_process = GetModuleHandle(NULL);

HMODULE this_process_again;
GetModuleHandleEx(0,0,&this_process_again);

В языке программирования Java классы можно загружать динамически с помощью ClassLoader объект. Например:

Class type = ClassLoader.getSystemClassLoader().loadClass(name);
Object obj = type.newInstance();

Механизм Reflection также предоставляет средства для загрузки класса, если он еще не загружен. Он использует загрузчик классов текущего класса:

Class type = Class.forName(name);
Object obj = type.newInstance();

Однако не существует простого способа контролируемой выгрузки класса. Загруженные классы могут быть выгружены только контролируемым способом, т.е. когда программист хочет, чтобы это произошло, если загрузчик классов, используемый для загрузки класса, не является загрузчиком системных классов и сам выгружается. При этом необходимо учитывать различные детали, чтобы гарантировать, что класс действительно разгружен. Это делает выгрузку классов утомительной.

Неявная выгрузка классов, т.е. неконтролируемая сборщиком мусора процедура, в Java менялась несколько раз. До Java 1.2. сборщик мусора мог выгрузить класс всякий раз, когда чувствовал, что ему нужно пространство, независимо от того, какой загрузчик классов использовался для загрузки класса. Начиная с Java 1.2 классы, загруженные через системный загрузчик классов, никогда не выгружались, а классы загружались через другие загрузчики классов только тогда, когда этот другой загрузчик классов был выгружен. Начиная с Java 6, классы могут содержать внутренний маркер, указывающий сборщику мусора, что они могут быть выгружены, если сборщик мусора желает это сделать, независимо от загрузчика классов, используемого для загрузки класса. Сборщик мусора может игнорировать эту подсказку.

Аналогично, библиотеки, реализующие собственные методы, загружаются динамически с помощью System.loadLibrary метод. Нет System.unloadLibrary метод.

Платформы без динамической нагрузки

[ редактировать ]

Несмотря на его распространение в 1980-х годах в Unix и Windows, некоторые системы по-прежнему предпочитали не добавлять динамическую загрузку или даже удалять ее. Например, Plan 9 от Bell Labs и его преемник 9front намеренно избегают динамического связывания, поскольку считают его «вредным». [9] Язык программирования Go , созданный некоторыми из тех же разработчиков, что и Plan 9, также не поддерживал динамическое связывание, но загрузка плагинов доступна начиная с Go 1.8 (февраль 2017 г.). Среда выполнения Go и все библиотечные функции статически скомпонованы в скомпилированный двоичный файл. [10]

См. также

[ редактировать ]
  1. ^ Jump up to: а б Autoconf, Automake и Libtool: динамическая загрузка
  2. ^ «Linux4U: динамическая загрузка ELF» . Архивировано из оригинала 11 марта 2011 г. Проверено 31 декабря 2007 г.
  3. ^ «Использование процедур, поставляемых CICS, для установки прикладных программ» .
  4. ^ «Запрос IBM CEMT NEWCOPY или PHASEIN завершается с ошибкой NOT FOR HOLD PROG — Соединенные Штаты» . 15 марта 2013 г.
  5. ^ Хо, В. Уилсон; Олссон, Рональд А. (1991). «Подход к настоящему динамическому связыванию». Программное обеспечение: практика и опыт . 21 (4): 375–390. CiteSeerX   10.1.1.37.933 . дои : 10.1002/спе.4380210404 . S2CID   9422227 .
  6. ^ «Поддержка динамических общих объектов (DSO) Apache 1.3» . Архивировано из оригинала 22 апреля 2011 г. Проверено 31 декабря 2007 г.
  7. ^ GCC 4.3.2 Параметры оптимизации: -fstrict-aliasing
  8. ^ Jump up to: а б POSIX-документация по dlopen() (выпуски 6 и 7).
  9. ^ «Динамическое соединение» . cat-v.org . 9перед . Проверено 22 декабря 2014 г.
  10. ^ «Перейти к часто задаваемым вопросам» .

Дальнейшее чтение

[ редактировать ]
  • Зильбершац, Авраам; Гэлвин, Питер Баер; Ганье, Грег (2005). «Глава 8.1.4 «Динамическая загрузка» и Глава 8.1.5 «Динамическое связывание и общие библиотеки» ». Концепции операционной системы . Дж. Уайли и сыновья . ISBN  978-0-471-69466-3 .
[ редактировать ]
Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: c793713e33dfcb714f3fc2b4df963d62__1720891080
URL1:https://arc.ask3.ru/arc/aa/c7/62/c793713e33dfcb714f3fc2b4df963d62.html
Заголовок, (Title) документа по адресу, URL1:
Dynamic loading - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)