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

StackOverflow https://stackoverflow.com/questions/140061

Вопрос

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

Это было полезно?

Решение

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

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

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

Динамические библиотеки в большинстве случаев считались лучшим подходом, но изначально у них был серьезный недостаток (ад Google DLL), который практически был устранен в более поздних ОС 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++ объясняет необходимые опции.

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

Библиотека — это единица кода, включенная в исполняемый файл вашего приложения.

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

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

Минусы dll:влияние на производительность загрузки dll и перебазирования кода;проблемы с версиями («ад 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]>

Программы C++ создаются в два этапа.

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

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

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

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

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

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

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

Ад DLL — это когда вам, вероятно, ДОЛЖНО было использовать статическую библиотеку, но вместо этого вы использовали dll, и не все exes совместимы с ней.

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

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

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

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

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

Статья Ульриха Дреппера о «Как писать общие библиотеки» также является хорошим ресурсом, в котором подробно описывается, как лучше всего использовать преимущества общих библиотек или того, что он называет «динамическими общими объектами» (DSO).Он больше фокусируется на общих библиотеках в ЭЛЬФ двоичный формат, но некоторые обсуждения подходят и для Windows DLL.

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

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

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

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

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

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

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

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

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

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

В нашем проекте мы используем много 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), которую вы хотите разделить на более управляемые библиотеки, сделайте их статическими библиотеками.Динамические библиотеки на самом деле ничего вам не дадут, и сюрпризов меньше - например, будет только один экземпляр синглтонов.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top