

20. Библиотеки динамической загрузки Windows
Статическая и динамическая компоновка
Вспомним старые добрые времена, когда операционная система MS-DOS и компьютеры с памятью 1 Мбайт удовлетворяли запросы подавляющего большинства пользователей. Как создавался загрузочный файл программы для MS-DOS? Вы готовили исходный текст приложения на своем любимом языке программирования (Си, Паскаль, Модула-2 и т. д.), затем транслировали его для получения объектного модуля. После этого в дело включался редактор связей, который компоновал объектные модули, полученные в результате трансляции исходных текстов и модули из библиотек объектных модулей, в один exe-файл. В процессе запуска файл программы загружался полностью в оперативную память и ему передавалось управление.
Таким образом, редактор связей записывал в файл программы все модули, необходимые для работы. Для обращения к внешним по отношению к программам модулям операционной системы MS-DOS и модулям BIOS использовался механизм программных прерываний. В любой момент времени в оперативной и постоянной памяти компьютера находился весь код, необходимый для работы как запущенной программы, так и самой операционной системы MS-DOS.
Если же программа была сложной и не помещалась целиком в 640 Кбайт памяти, на помощь приходили оверлеи, позволяющие загружать в оперативную память только те сегменты программы, которые требовались для решения текущей задачи.
В среде мультизадачной операционной системы, такой как Windows, OS/2 или UNIX, статическая компоновка неэффективна, так как приводит к неэкономному использованию очень дефицитного ресурса - оперативной памяти. Представьте себе, что в системе одновременно работают 5 приложений, и все они вызывают такие функции, как sprintf, memcpy, strcmp и т. д. Если приложения были собраны с использованием статической компоновки, в памяти будут находится одновременно 5 копий функции sprintf, 5 копий функции memcpy, и т. д (рис. 1). А ведь программы могут использовать и более сложные функции, например, предназначенные для работы с диалоговыми панелями или масштабируемыми шрифтами!
Очевидно, использование оперативной памяти было бы намного эффективнее, если бы в памяти находилось только по одной копии функций, а все работающие параллельно программы могли бы их вызывать.
Практически в любой многозадачной операционной системе для любого компьютера используется именно такой способ обращения к функциям, нужным одновременно большому количеству работающих параллельно программ.
При использовании динамической компоновки загрузочный код нескольких (или нескольких десятков) функций объединяется в отдельные файлы, загружаемые в оперативную память в единственном экземпляре. Программы, работающие параллельно, вызывают функции, загруженные в память из файлов библиотек динамической компоновки, а не из файлов программ.
Таким образом, используя механизм динамической компоновки, в загрузочном файле программы можно расположить только те функции, которые являются специфическими для данной программы. Те же функции, которые нужны всем (или многим) программам, работающим параллельно, можно вынести в отдельные файлы - библиотеки динамической компоновки, и хранить в памяти в единственном экземпляре (рис. 2). Эти файлы можно загружать в память только при необходимости, например, когда какая-нибудь программа захочет вызвать функцию, код которой расположен в библиотеке.
В операционной системе Windows версии 3.1 файлы библиотек динамической компоновки (DLL-библиотеки) имеют расширение имени dll, хотя можно использовать любое другое, например, exe. В первых версиях Windows DLL-библиотеки располагались в файлах с расширением имени exe. Возможно поэтому файлы krnl286.exe, krnl386.exe, gdi.exe и user.exe имеют расширение имени exe, а не dll, не смотря на то, что перечисленные выше файлы, составляющие ядро операционной системы Windows, есть ни что иное, как DLL-библиотеки.
Механизм динамической компоновки используется не только в системе Windows. Более того, он был изобретен задолго до появления Windows. Например, в мультизадачных многопользовательских операционных системах VS1, VS2, MVS, VM, созданных для компьютеров IBM-370 и аналогичных (вспомните добрым словом ушедшую в прошлое серию ЕС ЭВМ и операционные системы SVS, TKS, СВМ, МВС), код функций, нужных параллельно работающим программам, располагается в отдельных библиотеках и может загружаться при необходимости в специально выделенную общую область памяти. Операционная система OS/2 также работает с DLL-библиотеками.
Можно выделить несколько библиотек, составляющих ядро операционной системы, и основных библиотек с ключевыми дополнительными функциями.
Библиотеки Win32 API ядра операционной системы Windows 95/98:
KERNEL32.DLL: низкоуровневые функции управления памятью, задачами и другими ресурсами системы;
USER32.DLL: здесь в основном находятся функции управления пользовательским интерфейсом;
GDI32.DLL: библиотека Graphics Device Interface - разнообразные функции вывода на внешние устройства;
COMDLG32.DLL: функции, связанные с использованием диалоговых окон общего назначения.
Прежде всего для экономии памяти, так как все запущенные приложения могут использовать один модуль DLL-библиотеки, не включая стандартные функции в состав своих модулей.
С помощью DLL-библиотек можно организовать коллективное использование ресурсов или данных, расположенных в сегменте данных библиотеки. Более того, вы можете создать DLL-библиотеки, состоящие только из одних ресурсов, например, из пиктограмм или изображений bitmap. В состав Windows входит DLL-библиотека moricons.dll, состоящая из одних пиктограмм. Файлы с расширением fon представляют собой ни что иное, как DLL-библиотеки, содержащие шрифты в виде ресурса.
Функции, входящие в состав DLL-библиотеки, могут заказывать блоки памяти с атрибутом GMEM_SHARE. Такой блок памяти не принадлежит ни одному приложению и поэтому не освобождается автоматически при завершении работы приложения. Так как в Windows версии 3.1 все приложения используют общую глобальную память, блоки памяти с атрибутом GMEM_SHARE можно использовать для обмена данными между приложениями. Управлять таким обменом могут, например, функции, расположенные в соответствующей DLL-библиотеке. Однако в следующих версиях Windows каждое приложение будет работать в собственном адресном пространстве, поэтому для организации обмена данных между приложениями следует использовать специальный механизм динамического обмена данными DDE, который мы рассмотрим позже в отдельной главе.
Использование DLL-библиотек повышает модульность приложений и самой операционной системы Windows. С точки зрения приложения DLL-библиотека является не более чем набором функций с тем или иным интерфейсом, а также, возможно, набором ресурсов. Внутреннее "устройство" и алгоритмы работы функций, а также используемые функциями структуры данных полностью скрыты от приложения. Поэтому при внесении изменений или усовершенствований в DLL-библиотеки нет необходимости выполнять повторную сборку приложений (если не изменился интерфейс или набор функций, входящих в библиотеку).
Область применения DLL-библиотек достаточно широка. Помимо предоставления приложениям функций организации пользовательского интерфейса и реализации различных расширений Windows типа мультимедиа или систем управления базами данных, DLL-библиотеки необходимы для обеспечения ряда системных операций.
Разумеется, у динамической компоновки есть и свои недостатки.
Во-первых, DLL-библиотеки сложнее в разработке по сравнению с обычными библиотеками статической компоновки. Приходится принимать во внимание неравенство содержимого регистров DS и SS во время выполнения функций, расположенных в DLL-библиотеке, а также то, что DLL-библиотека имеет единственный сегмент данных, общий для всех приложений, вызывающих функции из библиотеки.
Во-вторых, в дополнение к exe-файлу вместе с приложением необходимо устанавливать один или несколько dll-файлов, что в некоторой степени усложняет процесс установки и сопровождения приложения. Может также возникнуть ситуация, при которой приложение не находит свою DLL-библиотеку, несмотря на то, что нужная библиотека есть на диске.
В-третьих, без тщательного планирования состава функций, включаемых в DLL-библиотеку, экономии памяти может и не получиться. В частности, возможна такая ситуация, когда и приложение, и DLL-библиотека пользуются одними и теми же функциями стандартной библиотеки компилятора в варианте статической компоновки.
Структура DLL-библиотеки
DLL-библиотека состоит из нескольких специфических функций и произвольного набора функций, выполняющих ту работу, для которой разрабатывалась данная библиотека. Как мы уже говорили, DLL-библиотека может иметь (а может и не иметь) сегмент данных и ресурсы.
В заголовке загрузочного модуля DLL-библиотеки описаны экспортируемые точки входа, соответствующие всем или некоторым определенным в ней функциям. Приложения могут вызывать только те функции DLL-библиотеки, которые экспортируются ей.
Заключение
DLL - одна из самых полезных и мощных возможностей, когда-либо добавленных в операционную систему Windows. Используя DLL, можно решать две главные задачи в разработке приложений: ограничения по памяти и проблемы с версиями. DLL - мощное средство в разработке приложений, дающее широкие просторы для творчества программистам.
Вспомним старые добрые времена, когда операционная система MS-DOS и компьютеры с памятью 1 Мбайт удовлетворяли запросы подавляющего большинства пользователей. Как создавался загрузочный файл программы для MS-DOS? Вы готовили исходный текст приложения на своем любимом языке программирования (Си, Паскаль, Модула-2 и т. д.), затем транслировали его для получения объектного модуля. После этого в дело включался редактор связей, который компоновал объектные модули, полученные в результате трансляции исходных текстов и модули из библиотек объектных модулей, в один exe-файл. В процессе запуска файл программы загружался полностью в оперативную память и ему передавалось управление.
Таким образом, редактор связей записывал в файл программы все модули, необходимые для работы. Для обращения к внешним по отношению к программам модулям операционной системы MS-DOS и модулям BIOS использовался механизм программных прерываний. В любой момент времени в оперативной и постоянной памяти компьютера находился весь код, необходимый для работы как запущенной программы, так и самой операционной системы MS-DOS.
Если же программа была сложной и не помещалась целиком в 640 Кбайт памяти, на помощь приходили оверлеи, позволяющие загружать в оперативную память только те сегменты программы, которые требовались для решения текущей задачи.
В среде мультизадачной операционной системы, такой как Windows, OS/2 или UNIX, статическая компоновка неэффективна, так как приводит к неэкономному использованию очень дефицитного ресурса - оперативной памяти. Представьте себе, что в системе одновременно работают 5 приложений, и все они вызывают такие функции, как sprintf, memcpy, strcmp и т. д. Если приложения были собраны с использованием статической компоновки, в памяти будут находится одновременно 5 копий функции sprintf, 5 копий функции memcpy, и т. д (рис. 1). А ведь программы могут использовать и более сложные функции, например, предназначенные для работы с диалоговыми панелями или масштабируемыми шрифтами!
Очевидно, использование оперативной памяти было бы намного эффективнее, если бы в памяти находилось только по одной копии функций, а все работающие параллельно программы могли бы их вызывать.
Практически в любой многозадачной операционной системе для любого компьютера используется именно такой способ обращения к функциям, нужным одновременно большому количеству работающих параллельно программ.
При использовании динамической компоновки загрузочный код нескольких (или нескольких десятков) функций объединяется в отдельные файлы, загружаемые в оперативную память в единственном экземпляре. Программы, работающие параллельно, вызывают функции, загруженные в память из файлов библиотек динамической компоновки, а не из файлов программ.
Таким образом, используя механизм динамической компоновки, в загрузочном файле программы можно расположить только те функции, которые являются специфическими для данной программы. Те же функции, которые нужны всем (или многим) программам, работающим параллельно, можно вынести в отдельные файлы - библиотеки динамической компоновки, и хранить в памяти в единственном экземпляре (рис. 2). Эти файлы можно загружать в память только при необходимости, например, когда какая-нибудь программа захочет вызвать функцию, код которой расположен в библиотеке.
В операционной системе Windows версии 3.1 файлы библиотек динамической компоновки (DLL-библиотеки) имеют расширение имени dll, хотя можно использовать любое другое, например, exe. В первых версиях Windows DLL-библиотеки располагались в файлах с расширением имени exe. Возможно поэтому файлы krnl286.exe, krnl386.exe, gdi.exe и user.exe имеют расширение имени exe, а не dll, не смотря на то, что перечисленные выше файлы, составляющие ядро операционной системы Windows, есть ни что иное, как DLL-библиотеки.
Механизм динамической компоновки используется не только в системе Windows. Более того, он был изобретен задолго до появления Windows. Например, в мультизадачных многопользовательских операционных системах VS1, VS2, MVS, VM, созданных для компьютеров IBM-370 и аналогичных (вспомните добрым словом ушедшую в прошлое серию ЕС ЭВМ и операционные системы SVS, TKS, СВМ, МВС), код функций, нужных параллельно работающим программам, располагается в отдельных библиотеках и может загружаться при необходимости в специально выделенную общую область памяти. Операционная система OS/2 также работает с DLL-библиотеками.
Можно выделить несколько библиотек, составляющих ядро операционной системы, и основных библиотек с ключевыми дополнительными функциями.
Библиотеки Win32 API ядра операционной системы Windows 95/98:
KERNEL32.DLL: низкоуровневые функции управления памятью, задачами и другими ресурсами системы;
USER32.DLL: здесь в основном находятся функции управления пользовательским интерфейсом;
GDI32.DLL: библиотека Graphics Device Interface - разнообразные функции вывода на внешние устройства;
COMDLG32.DLL: функции, связанные с использованием диалоговых окон общего назначения.
Прежде всего для экономии памяти, так как все запущенные приложения могут использовать один модуль DLL-библиотеки, не включая стандартные функции в состав своих модулей.
С помощью DLL-библиотек можно организовать коллективное использование ресурсов или данных, расположенных в сегменте данных библиотеки. Более того, вы можете создать DLL-библиотеки, состоящие только из одних ресурсов, например, из пиктограмм или изображений bitmap. В состав Windows входит DLL-библиотека moricons.dll, состоящая из одних пиктограмм. Файлы с расширением fon представляют собой ни что иное, как DLL-библиотеки, содержащие шрифты в виде ресурса.
Функции, входящие в состав DLL-библиотеки, могут заказывать блоки памяти с атрибутом GMEM_SHARE. Такой блок памяти не принадлежит ни одному приложению и поэтому не освобождается автоматически при завершении работы приложения. Так как в Windows версии 3.1 все приложения используют общую глобальную память, блоки памяти с атрибутом GMEM_SHARE можно использовать для обмена данными между приложениями. Управлять таким обменом могут, например, функции, расположенные в соответствующей DLL-библиотеке. Однако в следующих версиях Windows каждое приложение будет работать в собственном адресном пространстве, поэтому для организации обмена данных между приложениями следует использовать специальный механизм динамического обмена данными DDE, который мы рассмотрим позже в отдельной главе.
Использование DLL-библиотек повышает модульность приложений и самой операционной системы Windows. С точки зрения приложения DLL-библиотека является не более чем набором функций с тем или иным интерфейсом, а также, возможно, набором ресурсов. Внутреннее "устройство" и алгоритмы работы функций, а также используемые функциями структуры данных полностью скрыты от приложения. Поэтому при внесении изменений или усовершенствований в DLL-библиотеки нет необходимости выполнять повторную сборку приложений (если не изменился интерфейс или набор функций, входящих в библиотеку).
Область применения DLL-библиотек достаточно широка. Помимо предоставления приложениям функций организации пользовательского интерфейса и реализации различных расширений Windows типа мультимедиа или систем управления базами данных, DLL-библиотеки необходимы для обеспечения ряда системных операций.
Разумеется, у динамической компоновки есть и свои недостатки.
Во-первых, DLL-библиотеки сложнее в разработке по сравнению с обычными библиотеками статической компоновки. Приходится принимать во внимание неравенство содержимого регистров DS и SS во время выполнения функций, расположенных в DLL-библиотеке, а также то, что DLL-библиотека имеет единственный сегмент данных, общий для всех приложений, вызывающих функции из библиотеки.
Во-вторых, в дополнение к exe-файлу вместе с приложением необходимо устанавливать один или несколько dll-файлов, что в некоторой степени усложняет процесс установки и сопровождения приложения. Может также возникнуть ситуация, при которой приложение не находит свою DLL-библиотеку, несмотря на то, что нужная библиотека есть на диске.
В-третьих, без тщательного планирования состава функций, включаемых в DLL-библиотеку, экономии памяти может и не получиться. В частности, возможна такая ситуация, когда и приложение, и DLL-библиотека пользуются одними и теми же функциями стандартной библиотеки компилятора в варианте статической компоновки.
Структура DLL-библиотеки
DLL-библиотека состоит из нескольких специфических функций и произвольного набора функций, выполняющих ту работу, для которой разрабатывалась данная библиотека. Как мы уже говорили, DLL-библиотека может иметь (а может и не иметь) сегмент данных и ресурсы.
В заголовке загрузочного модуля DLL-библиотеки описаны экспортируемые точки входа, соответствующие всем или некоторым определенным в ней функциям. Приложения могут вызывать только те функции DLL-библиотеки, которые экспортируются ей.
Заключение
DLL - одна из самых полезных и мощных возможностей, когда-либо добавленных в операционную систему Windows. Используя DLL, можно решать две главные задачи в разработке приложений: ограничения по памяти и проблемы с версиями. DLL - мощное средство в разработке приложений, дающее широкие просторы для творчества программистам.