Когда использовать динамические и статические библиотеки


при создании библиотеки классов в C++ можно выбрать между dynamic (.DLL) и статические (.lib) и библиотек. В чем разница между ними и когда это целесообразно использовать?

18 396

18 ответов:

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

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

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

динамические библиотеки считались лучшим подходом большую часть времени, но первоначально у них был серьезный недостаток (Google DLL hell), который был устранен более поздними ОС Windows (в частности, Windows XP).

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

  • синглтоны: если что-то должно быть глобальным/статическим и уникальным, будьте очень осторожны, помещая его в статическую библиотеку. Если несколько DLL связаны с этой статической библиотекой, каждый из них получит свою собственную копию синглтона. Однако, если ваше приложение является одним EXE без пользовательские DLL, это не может быть проблемой.

  • удаление неиспользуемого кода: когда вы связываетесь со статической библиотекой, только части статической библиотеки, на которые ссылается ваша DLL/EXE, будут связаны с вашей DLL/EXE.

    например, если mylib.lib содержит a.obj и b.obj и ваша DLL / EXE только ссылается на функции или переменные из a.obj все b.obj будет отбрасываться компоновщиком. Если b.obj содержит глобальные / статические объекты, их конструкторы и деструкторы не будут выполняться. Если эти конструкторы/деструкторы имеют побочные эффекты, вы можете быть разочарованы их отсутствием.

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

  • отладочных символов: вам может понадобиться отдельный PDB для каждой статической библиотеки, или вы можете нужно, чтобы символы отладки были помещены в объектные файлы, чтобы они были свернуты в PDB для DLL/EXE. В документации по Visual C++ объясняется необходимые опции.

  • RTTI: вы можете в конечном итоге с несколькими type_info объекты для одного и того же класса, если вы связываете одну статическую библиотеку с несколькими DLL. Если ваша программа предполагает, что type_info это "синглтон" данных и использует &typeid() или type_info::before(), вы можете получить нежелательные и неожиданные результаты.

lib-это единица кода, которая входит в состав исполняемого файла приложения.

dll-это отдельная единица исполняемого кода. Она загружается в процесс только при вызове в этот код. Библиотека dll может использоваться несколькими приложениями и загружаться в несколько процессов, при этом на жестком диске остается только одна копия кода.

Dll pros: смогите быть использовано для того чтобы повторно использовать/делить код между несколькими продуктов; нагрузка в отростчатой памяти по требованию и может быть выгружен, когда это не требуется; может быть обновлен независимо от остальной части программы.

Dll минусы: влияние на производительность загрузки dll и перебазирования кода; проблемы с версиями ("dll hell")

Lib pros: нет влияния на производительность, так как код всегда загружается в процессе и не перебазируется; нет проблем с версиями.

Lib cons: executable / process "bloat" - весь код находится в вашем исполняемом файле и загружается при запуске процесса; нет повторного использования / совместного использования - каждый продукт имеет свою собственную копию кода.

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

например, если вы используете лицензионный код LGPL и статически связываетесь с библиотекой LGPL (и таким образом создаете один большой двоичный файл), ваш код автоматически становится открытым исходным кодом (свободный как в свободе) код LGPL. Если вы ссылаетесь на общие объекты, то вам нужно только LGPL улучшения / исправления ошибок, которые вы делаете в самой библиотеке LGPL.

Это становится гораздо более важным вопросом, если вы решаете, как скомпилировать вам мобильные приложения, например (в Android у вас есть выбор статического против динамического, в iOS вы этого не делаете - это всегда статично).


создание статической библиотеки

$$:~/static [32]> cat foo.c
#include<stdio.h>
void foo()
{
printf("\nhello world\n");
}
$$:~/static [33]> cat foo.h
#ifndef _H_FOO_H
#define _H_FOO_H

void foo();

#endif
$$:~/static [34]> cat foo2.c
#include<stdio.h>
void foo2()
{
printf("\nworld\n");
}
$$:~/static [35]> cat foo2.h
#ifndef _H_FOO2_H
#define _H_FOO2_H

void foo2();

#endif
$$:~/static [36]> cat hello.c
#include<foo.h>
#include<foo2.h>
void main()
{
foo();
foo2();
}
$$:~/static [37]> cat makefile
hello: hello.o libtest.a
        cc -o hello hello.o -L. -ltest
hello.o: hello.c
        cc -c hello.c -I`pwd`
libtest.a:foo.o foo2.o
        ar cr libtest.a foo.o foo2.o
foo.o:foo.c
        cc -c foo.c
foo2.o:foo.c
        cc -c foo2.c
clean:
        rm -f foo.o foo2.o libtest.a hello.o

$$:~/static [38]>

создание динамической библиотеки

$$:~/dynamic [44]> cat foo.c
#include<stdio.h>
void foo()
{
printf("\nhello world\n");
}
$$:~/dynamic [45]> cat foo.h
#ifndef _H_FOO_H
#define _H_FOO_H

void foo();

#endif
$$:~/dynamic [46]> cat foo2.c
#include<stdio.h>
void foo2()
{
printf("\nworld\n");
}
$$:~/dynamic [47]> cat foo2.h
#ifndef _H_FOO2_H
#define _H_FOO2_H

void foo2();

#endif
$$:~/dynamic [48]> cat hello.c
#include<foo.h>
#include<foo2.h>
void main()
{
foo();
foo2();
}
$$:~/dynamic [49]> cat makefile
hello:hello.o libtest.sl
        cc -o hello hello.o -L`pwd` -ltest
hello.o:
        cc -c -b hello.c -I`pwd`
libtest.sl:foo.o foo2.o
        cc -G -b -o libtest.sl foo.o foo2.o
foo.o:foo.c
        cc -c -b foo.c
foo2.o:foo.c
        cc -c -b foo2.c
clean:
        rm -f libtest.sl foo.o foo

2.o hello.o
$$:~/dynamic [50]>

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

Если есть два приложения, которые используют общий код, вы хотите, чтобы заставить эти приложения, чтобы меняться вместе, если они должны быть совместимы друг с другом? Затем используйте dll. Все exe-файлы будут использовать один и тот же код.

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

DLL hell-это когда вы, вероятно, должны были использовать статическую lib, но вместо этого вы использовали dll, и не все exes совместимы с ней.

программы на C++ строятся в два этапа

  1. компиляции создается объектный код (.obj)
  2. связывание-создает исполняемый код (.exe или .dll)

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

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

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

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

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

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

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

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

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

для отличного обсуждения этой темы есть чтение в этой статье от Солнца.

Он входит во все преимущества, включая возможность вставки вставляющих библиотек. Более подробно о вставке можно узнать в вот эту статью.

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

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

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

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

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

Если вы можете жить со статической библиотекой, перейдите к статической библиотеке.

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

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

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

  • медленный запуск (> 10 секунд)
  • DLL должны были быть версионными, так как windows загружает модули на уникальность имен. Собственные написанные компоненты в противном случае получили бы неправильную версию DLL (т. е. уже загруженную вместо собственной распределенной комплект)
  • оптимизатор может оптимизировать только в пределах границ DLL. Например, оптимизатор пытается разместить часто используемые данные и код рядом друг с другом, но это не будет работать через границы DLL

может быть, лучше было сделать все статическая библиотека (и поэтому у вас есть только один исполняемый файл). Это работает только в том случае, если не происходит дублирования кода. Тест, похоже, поддерживает это предположение, но я не смог найти официальную цитату MSDN. Так например сделать 1 exe с:

  • exe использует shared_lib1, shared_lib2
  • shared_lib1 использовать shared_lib2
  • shared_lib2

код и переменные shared_lib2 должны присутствовать в конечном объединенном исполняемом файле только один раз. Может ли кто-нибудь поддержать этот вопрос?

Я бы дал общее эмпирическое правило, что если у вас есть большая кодовая база, все они построены поверх библиотек более низкого уровня (например, Utils или GUI framework), которые вы хотите разделить на более управляемые библиотеки, а затем сделать их статическими библиотеками. Динамические библиотеки на самом деле ничего не покупают, и есть меньше сюрпризов-например, будет только один экземпляр singletons.

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