При создании библиотеки классов на C++ вы можете выбирать между динамическими (.dll, .so) и статическими (.lib, .a) библиотеками. В чем разница между ними и какие из них целесообразно использовать?





Если ваша библиотека будет совместно использоваться несколькими исполняемыми файлами, часто имеет смысл сделать ее динамической, чтобы уменьшить размер исполняемых файлов. В противном случае обязательно сделайте его статичным.
Есть несколько недостатков использования dll. Есть дополнительные накладные расходы на погрузку и разгрузку. Также существует дополнительная зависимость. Если вы измените DLL, чтобы сделать ее несовместимой с исполняемыми файлами, они перестанут работать. С другой стороны, если вы измените статическую библиотеку, ваши скомпилированные исполняемые файлы, использующие старую версию, не пострадают.
Статическая библиотека компилируется в клиент. .Lib используется во время компиляции, и содержимое библиотеки становится частью используемого исполняемого файла.
Динамическая библиотека загружается во время выполнения и не компилируется в исполняемый файл клиента. Динамические библиотеки более гибкие, поскольку несколько клиентских исполняемых файлов могут загружать DLL и использовать ее функциональные возможности. Это также сводит к минимуму общий размер и ремонтопригодность вашего клиентского кода.
Статические библиотеки увеличивают размер кода в вашем двоичном файле. Они всегда загружены, и какая бы версия кода вы ни скомпилировали, это та версия кода, которая будет выполняться.
Динамические библиотеки хранятся и контролируются отдельно. Возможно, загружается версия динамической библиотеки, отличная от исходной, поставляемой с вашим кодом если, обновление считается двоично-совместимым с исходной версией.
Кроме того, динамические библиотеки не обязательно загружаются - они обычно загружаются при первом вызове - и могут использоваться совместно компонентами, использующими одну и ту же библиотеку (несколько загрузок данных, одна загрузка кода).
В большинстве случаев динамические библиотеки считались лучшим подходом, но изначально у них был серьезный недостаток (ад Google DLL), который был почти устранен более поздними ОС Windows (в частности, Windows XP).
В Windows / Mac (без диспетчера пакетов) действительно нет веских причин использовать динамические библиотеки вместо статических. Поскольку библиотеки DLL Windows не могут быть перемещены, совместное использование кода часто не работает (и обычно каждое приложение в любом случае поставляется и использует свои собственные версии библиотеки). Единственное реальное преимущество состоит в том, что библиотеку легче обновлять.
Что конкретно вы имеете в виду под «перемещаемым»?
на Mac я использую много динамических библиотек. например, в mac os x есть встраивание sqlite3. Я создал программу с функцией базы данных sqlite3 для сохранения производительности. однако, поскольку оно редко используется, динамическое связывание экономит время компиляции, упрощает / ускоряет тестирование, однако, если бы я создавал версию выпуска, я думаю, что всегда использовал бы статическую библиотеку только в случае проблем с совместимостью
@Zifre: relocatable = может быть загружен по другому виртуальному адресу. DLL, безусловно, поддерживает это.
@dma_k: библиотеки DLL Windows могут быть загружены по разным адресам, но только потому, что компоновщик копирует весь код и изменяет номера адресов. В случае общих объектов все адресные ссылки являются относительными, поэтому несколько процессов могут совместно использовать одну и ту же память для общего объекта. Другими словами, в Windows 1 МБ DLL, используемая 3 программами, = 3 МБ. В Linux размер SO, используемый 3 программами, равен 1 МБ.
И в Windows, и в Linux существует концепция перемещения разделяемых библиотек по расписанию eli.thegreenplace.net/2011/08/25/… Самая большая вещь, которая позволяла позиционно-независимый код, не была чем-то особенным для Linux, а скорее относительной адресацией RIP, добавленной с набором инструкций x64; как Windows, так и Linux могут использовать относительную адресацию RIP, уменьшая количество исправлений при перемещении библиотек.
Статические библиотеки хуже взаимодействуют и их труднее использовать, например, в средах .NET.
Стоит упомянуть, что иногда из-за лицензионных ограничений вам необходимо динамически связывать свои приложения с библиотеками, если вы хотите, чтобы ваша кодовая база была закрытой, например, Qt с LGPL.
Будет ли разница в производительности между StaticLib и SharedLib после полной загрузки кода в процессе?
Статическая библиотека должна быть связана с финальным исполняемым файлом; он становится частью исполняемого файла и следует за ним, куда бы он ни пошел. Динамическая библиотека загружается каждый раз при выполнении исполняемого файла и остается отдельно от исполняемого файла в виде файла DLL.
Вы могли бы использовать DLL, если хотите иметь возможность изменять функциональные возможности, предоставляемые библиотекой, без необходимости повторно связывать исполняемый файл (просто замените файл DLL, не заменяя исполняемый файл).
Вы бы использовали статическую библиотеку всякий раз, когда у вас нет причин использовать динамическую библиотеку.
Вы также можете использовать DLL, когда несколько других приложений используют ту же функциональность - это может уменьшить занимаемую площадь.
Кроме того, расширение вашей первоначальной концепции, «подключаемой» архитектуры, в которой вы хотите разрешить добавленные / неизвестные функции позже без необходимости перестраивать или перевыпускать, может быть выполнено только с помощью динамических библиотек.
Статические библиотеки - это архивы, которые содержат объектный код для библиотеки, при связывании с приложением этот код компилируется в исполняемый файл. Общие библиотеки отличаются тем, что они не компилируются в исполняемый файл. Вместо этого динамический компоновщик ищет в некоторых каталогах необходимые библиотеки, а затем загружает их в память. Более одного исполняемого файла могут использовать одну и ту же общую библиотеку одновременно, что снижает использование памяти и размер исполняемого файла. Однако есть еще файлы, которые можно распространить вместе с исполняемым файлом. Вам необходимо убедиться, что библиотека установлена в системе использования где-то там, где компоновщик может ее найти, статическая компоновка устраняет эту проблему, но приводит к увеличению исполняемого файла.
Если библиотека статическая, то во время компоновки код связывается с вашим исполняемым файлом. Это делает ваш исполняемый файл больше (чем если бы вы пошли по динамическому маршруту).
Если библиотека является динамической, то во время компоновки в ваш исполняемый файл будут встроены ссылки на требуемые методы. Это означает, что вам необходимо отправить исполняемый файл и динамическую библиотеку. Вы также должны подумать, является ли общий доступ к коду в библиотеке безопасным, предпочтительным адресом загрузки среди прочего.
Если вы можете жить со статической библиотекой, перейдите к статической библиотеке.
Библиотека - это единица кода, связанная с исполняемым файлом вашего приложения.
DLL - это автономная единица исполняемого кода. Он загружается в процесс только при вызове этого кода. DLL может использоваться несколькими приложениями и загружаться в несколько процессов, сохраняя при этом только одну копию кода на жестком диске.
Dll плюсы: может использоваться для повторного использования / совместного использования кода несколькими продуктами; загружается в память процесса по запросу и может выгружаться, когда не требуется; можно обновлять независимо от остальной программы.
Dll минусы: влияние загрузки dll и перебазирования кода на производительность; проблемы с версиями ("ад dll")
Либеральные профи: не влияет на производительность, поскольку код всегда загружается в процессе и не обновляется; нет проблем с версией.
Lib cons: исполняемый файл / процесс "раздувание" - весь код находится в вашем исполняемом файле и загружается при запуске процесса; нет повторного использования / обмена - у каждого продукта есть собственная копия кода.
Перебазирование также можно выполнить во время сборки с помощью rebase.exe или путем передачи параметра / BASE в link.exe. Эффективность этого метода зависит от того, возникнут ли какие-либо неожиданные конфликты адресного пространства во время выполнения.
Если вы работаете над встраиваемыми проектами или специализированными платформами, статические библиотеки - единственный выход, во многих случаях их легче скомпилировать в ваше приложение. Кроме того, наличие проектов и make-файлов, в которых есть все, делает жизнь счастливее.
Вам следует тщательно обдумать изменения с течением времени, управление версиями, стабильность, совместимость и т. д.
Если есть два приложения, которые используют общий код, вы хотите заставить эти приложения изменяться вместе, на случай, если они должны быть совместимы друг с другом? Затем используйте dll. Все исполняемые файлы будут использовать один и тот же код.
Или вы хотите изолировать их друг от друга, чтобы вы могли изменить одно и быть уверенным, что не сломали другое. Затем используйте статическую библиотеку.
DLL, черт возьми, это когда вам, вероятно, СЛЕДУЕТ использовать статическую библиотеку, но вместо этого вы использовали dll, и не все exes совместимы с ней.
На самом деле компромисс, который вы делаете (в большом проекте), заключается в начальном времени загрузки, библиотеки будут связаны в то или иное время, необходимо принять решение, будет ли ссылка длиться достаточно долго, чтобы компилятор нуждался укусить пулю и сделать это заранее, или динамический компоновщик может сделать это во время загрузки.
Другие адекватно объяснили, что такое статическая библиотека, но я хотел бы указать на некоторые предостережения при использовании статических библиотек, по крайней мере, в 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(), вы можете получить нежелательные и неожиданные результаты.
Что касается синглтонов, не забывайте, что DLL может загружаться несколько раз (одна и та же версия или несколько версий), и по-прежнему нет гарантии синглтона.
Дополнительный момент об удалении кода, на который нет ссылки: для вызовов DLL также требуется фактический вызов, чтобы принудительно загрузить указанную DLL. Если добавить его в качестве ссылки, но затем исключить какой-либо вызов, который ссылается на него, вы все равно получите тот же результат, что и статическая библиотека, которая ничего не вызывает. Единственная разница в том, что на самом деле отправляется В обоих случаях статические конструкторы и деструкторы не срабатывают.
@ bk1e Этого не должно происходить. .a всегда будет содержать все символы, с которыми он был построен. Когда он статически связан с вашим приложением, да, будут связаны только те символы, которые используются.
Для отличного обсуждения этой темы прочтите эта статья от Sun.
Он имеет все преимущества, включая возможность вставки промежуточных библиотек. Подробнее о вставке можно найти в эта статья здесь.
Статья Ульриха Дреппера о «Как писать общие библиотеки» также является хорошим ресурсом, в котором подробно описывается, как лучше всего использовать преимущества общих библиотек или то, что он называет «динамическими совместно используемыми объектами» (DSO). В нем больше внимания уделяется разделяемым библиотекам в двоичном формате ELF, но некоторые обсуждения подходят и для Windows DLL.
Помимо технических последствий статических и динамических библиотек (статические файлы объединяют все в одну большую двоичную систему против динамических библиотек, которые позволяют разделять код между несколькими различными исполняемыми файлами), существует юридические последствия.
Например, если вы используете лицензионный код 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 (> 100). Эти DLL зависят друг от друга, поэтому мы выбрали настройку динамического связывания. Однако у него есть следующие недостатки:
Возможно, лучше было сделать все статической библиотекой (и, следовательно, у вас есть только один исполняемый файл). Это работает, только если не происходит дублирования кода. Тест, похоже, подтверждает это предположение, но я не смог найти официальную цитату MSDN. Так, например, создайте 1 exe с помощью:
Код и переменные shared_lib2 должны присутствовать в окончательном объединенном исполняемом файле только один раз. Кто-нибудь может поддержать этот вопрос?
Разве вы не хотели каким-то образом использовать некоторые предварительные директивы компилятора, чтобы избежать дублирования кода?
Предварительная компиляция Afaiac работает только для отдельных модулей (exe / dll / lib). Предварительная компиляция в первую очередь предназначена для ускорения компиляции, хотя она также предотвращает множественные включения в единицу компиляции. Однако включить охранников - лучший способ добиться этого эффекта.
Я бы дал общее практическое правило: если у вас большая кодовая база, построенная на основе библиотек нижнего уровня (например, фреймворка Utils или Gui), которые вы хотите разделить на более управляемые библиотеки, а затем сделайте их статическими библиотеками. Динамические библиотеки на самом деле ничего вам не покупают, и сюрпризов меньше - например, будет только один экземпляр синглтонов.
Если у вас есть библиотека, которая полностью отделена от остальной части кодовой базы (например, сторонняя библиотека), подумайте о том, чтобы сделать ее dll. Если это библиотека LGPL, вам может потребоваться использовать dll из-за условий лицензирования.
Программы на C++ строятся в два этапа
Статическая библиотека (.lib) - это просто набор файлов .obj и поэтому не является полной программой. Не прошел второй (связывающий) этап построения программы. С другой стороны, DLL похожи на exe и, следовательно, представляют собой законченные программы.
Если вы создаете статическую библиотеку, она еще не связана, и поэтому потребителям вашей статической библиотеки придется использовать тот же компилятор, который вы использовали (если вы использовали g ++, им придется использовать g ++).
Если вместо этого вы создали dll (и построили ее правильно), вы создали полную программу, которую могут использовать все потребители, независимо от того, какой компилятор они используют. Однако существует несколько ограничений на экспорт из dll, если требуется совместимость с кросс-компилятором.
Это для меня новость. Какие ограничения есть у кросс-компиляторов при использовании DLL? Сборка программиста без использования того же набора инструментов кажется огромным плюсом для DLL.
Это информативный ответ. Добавление небольшого предостережения: consumers of your static library will have to use the same compiler that you used, если статическая библиотека использует библиотеку C++, например #include <iostream>.
нельзя использовать C++ dll, если не используется тот же компилятор (поскольку нет стандартного C++ abi, символы искажаются по-разному). И dll, и клиентский модуль должны использовать один и тот же компилятор и одинаковые настройки сборки.
Следует отметить, что есть также что-то, что называется "Импортировать библиотеку", проверка stackoverflow.com/questions/3573475/…