Обмен данными между процессами через отображаемый в память файл
Так как один и тот же файл может быть отображен в память несколькими процессами, и система поддерживает когерентность таких отображений, то механизм отображения файлов в память может использоваться для обмена данными между процессами. Кроме того, в операционных системах Windows все остальные механизмы обмена данными между процессами базируются на отображении файлов в память. Поэтому можно сказать, что отображение файлов в память обеспечивает наилучшую производительность по сравнению со всеми остальными способами обмена данными между процессами.
Концепция динамически подключаемых библиотек
Динамически подключаемая библиотека (DLL, Dynamic Link Library) представляет собой программный модуль, который может быть загружен в виртуальную память процесса как статически, во время создания исполняемого модуля процесса, так и динамически, во время исполнения процесса операционной системой. Программный модуль, оформленный в виде DLL, хранится на диске в виде файла, который имеет расширение dll, и может содержать как функции, так и данные. Для загрузки DLL в память используется механизм отображения файлов в память.
Динамически подключаемые библиотеки предназначены, главным образом, для разработки функционально-замкнутых библиотек функций, которые могут использоваться разными приложениями. Это позволяет снизить затраты на разработку программного обеспечения, т. к. один и тот же программный код может использоваться разными разработчиками. Кроме того, динамически подключаемые библиотеки позволяют уменьшить объем используемой физической памяти при одновременной работе нескольких приложений, которые используют одну и ту же библиотеку. Это достигается благодаря механизму проецирования DLL в виртуальную память процессов, т. к. в этом случае все приложения разделяют один и тот же экземпляр исполняемого кода DLL, загруженный в физическую память.
В связи с последним предложением следует сделать небольшое замечание. Исполняемые файлы и файлы динамических библиотек, т. е. файлы с расширениями ехе и dll соответственно разбиты на разделы, каждый из которых содержит данные только определенного типа. Один из этих разделов содержит только исполняемый код приложения или динамически подключаемой библиотеки. Вот этот раздел и хранится в физической памяти в одном экземпляре и отображается в адресное пространство всех процессов. Те же разделы, которые содержат данные, хранятся для каждого процесса в отдельном экземпляре. Поэтому получается, что процессы совместно разделяют исполняемый код из динамически подключаемой библиотеки, но каждый процесс имеет свой набор переменных из этой библиотеки.
Создание DLL
Создаются DLL подобно обычным исполняемым модулям, но при этом в среде разработки Visual Studio необходимо выбрать проект типа Win32 Dynamic-Link Library. Как и каждая программа на языке программирования C++, динамически подключаемая библиотека должна иметь главную функцию, которая отмечает точку входа в программу при ее исполнении операционной системой. В отличие от исполняемых модулей, в которых эта функция называется main, в DLL главная функция называется DllMain и вызывается операционной системой при загрузке DLL в адресное пространство процесса и при создании этим процессом нового потока. Главное назначение функции DllMain заключается в инициализации DLL при ее загрузке, а также захвате и освобождении необходимых ресурсов при создании и завершении нового потока в процессе. Эта функция имеет следующий прототип:
BOOL WINAPI DllMain(
HINSTANCE hinstDLL, // дескриптор DLL
DWORD fdwReason, // флаг причины вызова функции DllMain
LPVOID lpvReserved // зарезервировано Windows
);
При успешном завершении функция DllMain должна вернуть значение true, а в случае неудачи — значение false. Параметры функции DllMain имеют следующее назначение.
В параметре hinstDLL операционная система Windows передает дескриптор DLL, который фактически равен виртуальному адресу, по которому загружена DLL.
Параметр fdwReason может иметь одно из следующих значений, которое указывает на причину, по которой операционная система вызывает функцию DllMain:
□ dll_process_attach — DLL загружена в адресное пространство процесса;
□ DLL_THREAD_ATTACH — в процессе создан новый поток и функция DllMain вызывается в контексте этого потока;
П DLL_THREAD_DETACH — в процессе завершается поток и функция DllMain вызывается в контексте этого потока;
□ dll_process_detach — DLL выгружается из адресного пространства процесса.
Параметр lpvReserved совместно со значением параметра fdwReason отмечает способ загрузки и выгрузки DLL из адресного пространства процесса. Если значение параметра fdwReason равно dll_process_attach, то значение параметра lpvReserved, равное null, указывает на то, что DLL загружается динамически при помощи одной из функций LoadLibrary или LoadLibraryEx. Любое другое значение параметра lpvReserved в этом случае указывает на то, что DLL загружается статически.
Если значение параметра fdwReason равно dll_process_detach, то значение параметра lpvReserved, равное null, указывает на то, что DLL выгружается динамически при помощи функции FreeLibrary. Любое другое значение параметра lpvReserved в этом случае указывает на то, что DLL выгружается при завершении процесса.
Сделаем следующее замечание относительно функции DllMain. Если эта функция не реализована в DLL, то компилятор автоматически сгенерирует эту функцию, которая всегда будет возвращать значение true.
Теперь рассмотрим, как оформляются функции и переменные, которые DLL предоставляет в использование своим клиентам. Во-первых, заметим, что такие функции и переменные называются экспортируемыми. Для того чтобы сделать функцию или переменную экспортируемой, нужно определить их с модификатором extern "С" и квалификатором declspec(dllexport).
Кроме того, экспортируемая переменная должна быть инициализирована. Модификатор extern "С" указывает компилятору на то, что функция или переменная должна иметь имя в стиле языка программирования С. То есть имя функции или переменной не будет искажаться путем добавления к нему обозначений типов данных из сигнатуры функции или определения переменной. Модификатор decispec(diiexport) указывает компилятору на то, что данная функция или переменная будет экспортироваться из DLL.