Зачем нужно переносить часть кода в библиотеки отдельные проекты компилирующиеся в dll файлы

Обновлено: 06.07.2024

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

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

Различия между динамической и статической компоновкой

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

Различия между приложениями и библиотеками DLL

Хотя и библиотеки DLL, и приложения являются исполняемыми модулями, они отличаются некоторыми особенностями. Наиболее очевидное различие заключается в том, что библиотеку DLL нельзя запустить. С точки зрения системы, между приложениями и библиотеками DLL имеется два существенных различия.

В системе может одновременно выполняться несколько экземпляров приложения. Экземпляр библиотеки DLL может быть только один.

Преимущества использования библиотек DLL

Динамическая компоновка кода и ресурсов имеет некоторые преимущества над статической.

Динамическая компоновка экономит память и сокращает подкачку. Многие процессы могут использовать библиотеку DLL совместно, одновременно обращаясь к одной доступной только для чтения копии ее частей в памяти. В отличие от этого, каждое приложение, созданное с помощью библиотеки статической компоновки, имеет полную копию кода библиотеки, которую система Windows должна загрузить в память.

Динамическая компоновка экономит место на диске и пропускную способность. Несколько приложений могут совместно использовать одну копию библиотеки DLL на диске. В отличие от этого, каждое приложение, созданное с помощью библиотеки статической компоновки, имеет код библиотеки, связанный с исполняемым образом. Это увеличивает занимаемое на диске место и используемую для передачи данных пропускную способность.

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

С помощью библиотек DLL можно оказывать послепродажную поддержку. Например, библиотеку DLL драйвера дисплея можно изменить так, чтобы она поддерживала дисплей, который не был доступен на момент предоставления приложения.

С помощью явной компоновки можно обнаруживать и загружать библиотеки DLL во время выполнения. Например, это могут быть расширения приложения, которые добавляют новые функциональные возможности без повторной сборки и развертывания приложения.

Динамическая компоновка упрощает поддержку приложений, написанных на разных языках программирования. Программы, написанные на разных языках программирования, могут вызывать одну и ту же функцию в библиотеке DLL при условии соблюдения соглашения о ее вызове. Программы и функция в библиотеке DLL должны отвечать следующим требованиям к совместимости: ожидаемый функцией порядок передачи аргументов в стек; выполнение очистки стека функцией или приложением; передача аргументов в регистрах.

Динамическая компоновка обеспечивает механизм для расширения классов библиотеки Microsoft Foundation Classes (MFC). На основе существующих классов MFC можно создавать производные классы и помещать их в библиотеку расширения DLL, используемую приложениями MFC.

Динамическая компоновка упрощает создание международных версий приложения. Библиотеки DLL — это удобный способ предоставления ресурсов для конкретных языковых стандартов, благодаря чему значительно упрощается создание международных версий приложения. Вместо предоставления множества локализованных версий приложения можно поместить строки и изображения для каждого языка в отдельную библиотеку DLL ресурсов. Затем приложение может загружать ресурсы для нужного языкового стандарта во время выполнения.

Возможным недостатком использования библиотек DLL является то, что приложения не являются автономными. Они требуют наличия отдельного модуля DLL, которое должно проверяться в процессе установки.

Дополнительные сведения о создании и использовании библиотек DLL

В приведенных ниже статьях приводятся подробные сведения о создании библиотек DLL на C и C++ в Visual Studio.

Пошаговое руководство: Создание и использование библиотеки DLL (C++)
Описывает создание и использование библиотек DLL при помощи Visual Studio.

Виды библиотек DLL
Предоставляет сведения о различных типах библиотек DLL, которые доступны для сборки.

Вопросы и ответы по библиотекам DLL
Ответы на часто задаваемые вопросы о библиотеках DLL.

Связывание исполняемого файла с библиотекой DLL
Описание явного и неявного соединения с библиотекой DLL.

Инициализация библиотеки DLL
Описывается код инициализации библиотеки DLL, который должен выполняться при загрузке библиотеки DLL.

Библиотеки DLL и поведение библиотеки времени выполнения Visual C++
Описывается последовательность запуска библиотеки DLL средой выполнения.

Функции LoadLibrary и AfxLoadLibrary
Описывается использование функций LoadLibrary и AfxLoadLibrary для явной связи с библиотекой DLL во время выполнения.

Функция GetProcAddress
Описывается использование GetProcAddress для получения адреса экспортированной функции в DLL.

Функции FreeLibrary и AfxFreeLibrary
Описывается использование функций FreeLibrary и AfxFreeLibrary , когда модуль DLL больше не нужен.

Порядок поиска библиотеки динамической компоновки (DLL)
Описание пути поиска, который операционная система Windows использует для поиска библиотеки DLL в системе.

Состояния модулей обычной библиотеки DLL MFC, динамически связанной с MFC
Описываются состояния модулей обычной библиотеки DLL, динамически связываемой с MFC.

Библиотеки DLL для расширения MFC
Описываются библиотеки DLL, которые обычно реализуют классы многократного использования, производные от существующих классов MFC.

Создание библиотек DLL, содержащих только ресурсы
Библиотека DLL, содержащая только ресурсы, например значки, растровые изображения, строки и диалоговые окна.

Локализованные ресурсы в приложениях MFC: вспомогательные библиотеки DLL
Расширенная поддержка библиотек спутниковой связи DLL и содержит возможность, которая позволяет создавать приложения, локализированные на различные языки.

Импорт и экспорт
Импортирование открытых символов в приложение или экспортирование функций из библиотеки DLL

Технология Active и библиотеки DLL
Размещение серверов объектов внутри библиотеки DLL.

Автоматизация в библиотеке DLL
Параметр автоматизации в решениях мастера библиотек DLL MFC.

Соглашения об именовании библиотек DLL MFC
Способ встраивания библиотек DLL в MFC, опираясь на четко структурированное соглашение об именовании.

Связанные разделы

Использование MFC как части библиотеки DLL
Описываются постоянные библиотеки DLL MFC, которые позволяют использовать библиотеку MFC как часть библиотеки динамической компоновки Windows.

Я видел и делал много небольших продуктов, в которых одна и та же часть программного обеспечения разделена на один исполняемый файл и несколько DLL, и эти DLL - это не просто общие библиотеки, созданные кем-то другим, а библиотеки, которые созданы исключительно для этого программного обеспечения той же командой разработчиков. (Я не говорю здесь о крупномасштабных продуктах, которым просто требуются сотни библиотек DLL и которые широко используются совместно с другими продуктами.)

Я понимаю, что разделение кода на несколько частей, каждая из которых компилируется в отдельную DLL, - это хорошо с точки зрения разработчика . Это означает, что:

  • Если разработчик меняет один проект, он должен перекомпилировать только этот и зависимые, что может быть намного быстрее.
  • Проект может быть выполнен одним разработчиком в команде, в то время как другие разработчики будут просто использовать предоставленные интерфейсы, не вмешиваясь в код.
  • Иногда автоматические обновления программного обеспечения могут выполняться быстрее, что снижает нагрузку на сервер.

Но что насчет конечного пользователя? Разве это не плохо - поставить часть программного обеспечения, состоящую из одного EXE и нескольких DLL, когда все можно сгруппировать вместе? После всего:

  • Пользователь может даже не понимать, что это за файлы и почему они заполняют память на его жестком диске,
  • Пользователь может захотеть переместить программу, например, сохранить ее на USB-накопителе. Наличие одного большого исполняемого файла упрощает задачу,
  • Большинство антивирусных программ проверяют каждую DLL. Проверка одного исполняемого файла будет намного быстрее, чем исполняемый файл меньшего размера и десятки библиотек.
  • Использование DLL делает некоторые вещи медленнее (например, в .NET Framework необходимо найти "хорошую" библиотеку и проверить, подписана ли она),
  • Что произойдет, если DLL будет удалена или заменена плохой версией? Каждая программа справляется с этим? Или он вылетает, даже не объясняя, что с ним не так?
  • Наличие одного большого исполняемого файла дает некоторые другие преимущества.

Итак, не лучше ли с точки зрения конечных пользователей для программ малого и среднего размера предоставлять один большой исполняемый файл? Если да, то почему нет инструментов, позволяющих сделать это легко (например, волшебный инструмент, интегрированный в общие IDE, который компилирует все решение в один исполняемый файл, конечно, не каждый раз, а по запросу или во время развертывания).

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

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

Еще несколько причин для DLL

Некоторые библиотеки плохо работают вместе в одной сборке, но их можно заставить работать в DLL (например, одна DLL может использовать WTL3, а другая требует WTL8).

Некоторые из DLL могут содержать компоненты для загрузки в другие исполняемые файлы (глобальные перехватчики, расширения оболочки, надстройки браузера).

Некоторые из DLL могут быть сторонними, доступными только как DLL.

Возможно повторное использование внутри компании - даже если вы видите только один «общедоступный» продукт, он может использоваться в десятке внутренних проектов, использующих эту DLL.

Некоторые из DLL могли быть созданы в другой среде, недоступной для всех разработчиков в компании.

Автономный EXE и установленный продукт
Многие продукты в любом случае не будут работать как отдельные исполняемые файлы. Они требуют установки, и пользователь не должен касаться вещей, которых он не должен трогать. Наличие одного или нескольких двоичных файлов не имеет значения.

Влияние времени сборки
Возможно, вы недооцениваете влияние времени сборки и поддержания стабильной сборки для больших проектов. Если сборка занимает даже 5 минут, вы могли бы эпемистически назвать это «заставлять разработчиков думать наперед, вместо того, чтобы возиться, пока все не станет работать нормально». Но это серьезно поедает время и серьезно отвлекает.

Трудно улучшить время сборки одного проекта. При работе с VC9 параллелизация сборки в рамках одного проекта шатка, как и инкрементный компоновщик. Время соединения особенно трудно «оптимизировать» на более быстрых машинах.

Независимость разработчика
Еще одна вещь, которую можно недооценить.
Чтобы использовать DLL, вам понадобятся .dll и .h. Чтобы скомпилировать и связать исходный код, вам обычно нужно настроить подключаемые каталоги, выходные каталоги, установить сторонние библиотеки и т. Д. Это действительно больно.

Да, это лучше ИМХО - и я всегда использую статические ссылки именно по тем причинам, которые вы указываете, везде, где это возможно. Многие причины, по которым было изобретено динамическое связывание (например, для экономии памяти), больше не применяются. OTOH, есть архитектурные причины, например архитектура плагинов, почему динамическое связывание может быть предпочтительнее статического.

Я думаю, что ваше общее мнение о тщательном рассмотрении окончательной упаковки результатов сделано правильно. В случае с JavaScript такая упаковка действительно возможна, а сжатие имеет существенное значение.

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

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

Помните, что мы в StackOverflow - пользователи «выше среднего». Сколько у вас (не компьютерных) членов семьи и друзей, которые действительно оценили бы возможность установки своего программного обеспечения на USB-накопитель?

Большие преимущества для dll связаны с введением границ и независимости.

Например, в C / C ++ видны только экспортированные символы. Представьте модуль A с глобальной переменной «scale» и модуль B с другой глобальной переменной «scale», если вы сложите все вместе, вы пойдете в беду; в этом случае вам может помочь dll.

Вы можете распространять эту dll как компонент для клиентов без точно таких же параметров компилятора / компоновщика; и часто это хороший способ межъязыкового взаимодействия.

Выполнил много проектов, никогда не встречал конечного пользователя, у которого были бы проблемы с некоторыми файлами dll, находящимися на его ящике.

Как разработчик я бы сказал: да, это может иметь значение. Как конечный пользователь, которому не все равно .

Как лучше делить большой проект на либы или Длл и почему? Или как-то ещё?

Присоединяюсь. Одно и другое ортогонально.
Искренне недоумеваю почему длл до сих пор считается круто - их изобрели почти миллион лет назад :)

В некоторых играх (Hitman, например) граф. движок делают разными dll (под OpenGL и под DirectX).
Загрузка нужной меняется изменением строчки в конфиге.

Pa3DBaKp9IK

Раньше использовал dll дабы вынести рендереры (opengl, directx) из игры и динамически их подключать при инициализации графической части. сейчас это не практикую, ибо использую только OpenGL.
Но имхо искуственно разбивать проект. а что это должно дать на выходе. простоту отладки, или что? Честно говоря не совсем понял смысла.

R.Ambersky
Тоже самое можно сделать и с либом. запихнуть в в ЕХЕ. Единственное чем лучше ДЛЛ это тем, что её можно подменить(для новых версий удобнее)

>Присоединяюсь. Одно и другое ортогонально.
большой проект и DLL?

>Искренне недоумеваю почему длл до сих пор считается круто -
>их изобрели почти миллион лет назад :)
Ну для плугинов в тулзах таки удобно.

А application'ы (и игру в том числе) лучше собирать в single-exe из нескольких либ. ИМХО разумеется. Я ровно один раз в жизни попытался выпихнуть пару общих частей "движка" и больше так никогда не делал -- статическими либами удобнее гораздо.
Плюс не на всех платформах есть механизм DLL.

Время линковки, думаю, не критично -- приложение из полутора десятка либ линкуется не больше 2-3 секунд с -incremental:no (PM1.7/512/винт на 4200об -- нотбук,далеко не топовый).

Всякие преимущества запихивания кода в DLL -- модульность, легко-подменяемость уходят в никуда в реальных условиях разработки, когда _интерфейсы_ меняются достаточно часто.
Например в тулзе с

20 плугинами интерфейсы на моей памяти менялись около 10 раз -- это значит нужно было пересобирать все плугины и всем обновлять бинарники.
Если бы тупо следовали идеологии СОМ с её "interface is frozen" то имели бы груду (ненужного) кода для поддержки IPlugin1,Iplugin2. Iplugin10.

а по мне так удобно вынести второстепенные функции в длл. типа надо там текстуру подгрузить. вместо того чтоб забивать лишним кодом основное прилоение.. берешь в длл пихаешь, и тока индекс указываешь.

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

вообщем длл совсем не зло. )

Ghost Dragon
>>Присоединяюсь. Одно и другое ортогонально.
>большой проект и DLL?
Правильное разделение и структурирование кода в виде набора модулей и вопрос использовать или нет DLL.
Я не спорю что для DLL есть целый ряд применений, но я в корне не согласен с подходом, при котором для каждого квадратика с этой диаграмы создается DLL, потому что это круто. Ясен пень что если мы используем сторонние компоненты, скорее всего динамические библиотеки будут - потому что вендор распространяет в таком виде свой код. Когда же с самого начала всё разносится по DLL просто так, причем объявляется в качестве бенифита легкая заменяемость составных частей, это слегка странно. Интересно какой юзкейс при котором проявляются преимущества такой гибкости (не надо мне сейчас говорить про плугины)?

Потом самое главное, если сильно с самого начала не накосячить из любой (своей) библиотеки можно сделать DLL и всё продолжит работать (всё компилируется одним компилятором, и тд). Так что о вопросе использовать или нет DLL на этапе декомпозиции можно не думать.

AcMan
>а по мне так удобно вынести второстепенные функции в длл. типа надо там текстуру подгрузить. вместо того чтоб забивать лишним кодом основное прилоение.. берешь в длл пихаешь, и тока индекс указываешь.
>или там просчет какой сделать надо, ессесно вычисления первой необходимости в ехе держать, а вот чтото фоновое, или громоздкое.
Что это?

Как вы предлагаете без длл и либ?
Когда проект разростается время компиляции становится очень большим. При использовании отдельных модулей можно компилять только используемую сейчас часть.Мода здесь не причём.

>Когда же с самого начала всё разносится по DLL просто так,
>причем объявляется в качестве бенифита легкая заменяемость составных частей,
>это слегка странно.
я это и говорил, что преимущества на практике отсутствуют -- гемороя больше.

>Интересно какой юзкейс при котором проявляются преимущества такой гибкости (не надо мне сейчас говорить про плугины)?
Вот и мне интересно.
Единственное разумное применение (из моей практики) -- плугины в тулзах :)

>или там просчет какой сделать надо, ессесно вычисления первой необходимости в ехе >держать, а вот чтото фоновое, или громоздкое.
Я что-то фоновое или громоздкое должно считаться offline -- не нужно этим в игре заниматься (типа OGG в WAV распаковывать на старте).

>Когда проект разростается время компиляции становится очень большим.
>При использовании отдельных модулей можно компилять только используемую сейчас часть.
Так а кто мешает эти отдельные модули в статические либы положить? Они и будут компилироваться по отдельности.

Pa3DBaKp9IK
>Как вы предлагаете без длл и либ?
>Когда проект разростается время компиляции становится очень большим. При
>использовании отдельных модулей можно компилять только используемую сейчас
>часть.Мода здесь не причём.
Если ты меняешь глобальный header, то вынужден все равно пересобирать все библиотеки. Поэтому группировка по либам или сведение всего в одну exe-шку - твое личное дело. Если lib файлы подключены как проекты в workspace, время проверки и сборки практически не меняется.
Пример. American Chopper от Крейта компилировался в single exe. При этом на PC платформе была всего одна статическая либа (Engine) и exe (Game). На PS2 вообще _все_ исходники игры (более 800 файлов) были собраны в один проект.

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

Возможно использование DLL в играх излишне.
Возможно - потому что в написании игр я участия никогда не принимал. Но игра - это проект с фиксированным "циклом жизни". Его создают, отлаживают, продают, а после этого этапа он существует только в виде конечного числа патчей и bug-fixes. Если же такого знаменательного (и радостного для любого программиста) события, как завершение проекта в обозримом будущем (

10 лет) ждать не приходится и проект постоянно обрастает дополнительными возможностями (но при этом не изменяет своей основной структуры/идеологии), то разбиение на отдельные модули оправдано.

А еще - легче заказчику выслать dll в 50 кб, чем исправленный 7-8 мегабайтный exe.

Ghost2
>dDIMA
>и проект постоянно обрастает дополнительными возможностями . то разбиение на отдельные модули оправдано.
Ты сам себе противоречишь. Хочешь неизменяемые Dll и "обрастание дополнительными возможностями"? А если dll тоже меняются, то какой в этом смысл?

>А еще - легче заказчику выслать dll в 50 кб, чем исправленный 7-8 мегабайтный exe.
Патчи никто еще не отменял :). А отсылка исправленного dll нескольким заказчикам, у каждого из которых произвольное сочетание патчей, приводит к фантастическим результатам. Я об этом писал в первом посте (вторая ссылка на ЖЖ), когда говорил об отказе Микрософта от shared dll.

>А еще - легче заказчику выслать dll в 50 кб, чем исправленный 7-8 мегабайтный exe.
ага, а если интерфейсы поменялись, то нужно слать 50кб DLL _и_ мегабайтный ЕХЕ.

Более того патч (к игре) может включать в себя изменения и контента тоже -- 50К уже не отмажешся.

Есть ли смысл создавать такую большую dll (группы разбиты на классы для удобства) или лучше создать свою dll для каждой группы?

На какие критерии ориентироваться при разбиении на dll?

Какие есть плюсы и минусы у разбиения?

Если можно, то поподробнее, с объяснением.


30.6k 18 18 золотых знаков 75 75 серебряных знаков 98 98 бронзовых знаков


Вопрос не имеет решения в текущем виде. Само по себе число в 200 функций - это нормально, бывает и больше. Но принимать подобные решения на основе одного только числа функций - нельзя. Лучший способ организации кода зависит от задачи, которую он решает - а ее-то вы нам и не назвали. Ну и смысл минусовать-закрывать? Нормальный вопрос, хоть и несколько общий. На него же можно нормально ответить. Для начинающих программистов такие вещи неочевидны, потому что факторов не один и не два.

Разбивать на разные динамические библиотеки имеет смысл, если вам это реально нужно. Возможные причины:

Разные приложения будут использовать разные библиотеки, поэтому подключать к каждому монструозную библиотеку может быть нерационально. Чем больше библиотека, тем больше времени занимает JIT, тем медленнее скачивание и т. п.

С другой стороны, в каком-нибудь толстом приложении это имеет не такое большое значение, потому что толстый инсталлятор не будут скачивать через тонкий канал, инсталлятор может прогнать JIT при установке и т. п.

Разделение на библиотеки и явное указание зависимостей между ними заставляет разработчиков аккуратнее следить за разделением кода. Если приложение разрабатывается большой командой разной степени профессионализма, то может быть удобно раскидать логические модели физически.

Например, при реализации паттерна MVVM явно выделить View в отдельную сборку, чтобы никто не смел добавлять зависимостей между View и Model.

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

Если библиотеки имеют разные зависимости, то можно разделить библиотеки по зависимостям.

Не надо забывать и о минусах:

Если библиотека одна, то она быстрее компилируется и собирается. Одна библиотека на 100 классов соберётся быстрее, чем 10 библиотк на 10 классов каждая.

Легче подключить одну библиотеку, чем десять, и разруливать зависимости между ними и с внешними зависимостями.

Читайте также: