Коли використовувати динамічні та статичні бібліотеки


437

Створюючи бібліотеку класів у C ++, ви можете вибирати між динамічними ( .dll, .so) та статичними ( .lib, .a) бібліотеками. У чому різниця між ними і коли доцільно використовувати які?


2
Слід зазначити, що є також щось, що називається "Бібліотека імпорту", перевірити stackoverflow.com/questions/3573475/…
Wakan Tanka

Відповіді:


299

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

Динамічні бібліотеки зберігаються та переосмислюються окремо. Можлива завантаження версії динамічної бібліотеки, яка не була оригінальною та постачалася разом із вашим кодом, якщо оновлення вважається бінарним, сумісним із початковою версією.

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

Динамічні бібліотеки вважалися кращим підходом у більшості випадків, але спочатку вони мали головний недолік (Google DLL пекло), який був ліквідований більш новими ОС Windows (зокрема Windows XP).


71
У Windows / Mac (без менеджера пакунків) дійсно немає вагомих причин використовувати динамічні бібліотеки над статичними. Оскільки DLL-файли Windows не переміщуються, обмін кодом часто не працює (і зазвичай кожне додаток постачає та використовує власні версії бібліотеки в будь-якому разі). Єдина реальна користь - це простіше оновити бібліотеку.
Зіфре

5
на mac я використовую багато динамічних бібліотек. наприклад, mac os x має вбудовувати sqlite3. Я створив програму, яка має функцію бази даних sqlite3 для зберігання продуктивності. однак, оскільки рідко використовується динамічне посилання, економить час компіляції, робить тестування простішим / швидшим, однак, якби я будував версію версії, я думаю, я завжди використовував би статичну бібліотеку на випадок проблем із сумісністю
ReachConnection

6
@Zifre: relocatable = може бути завантажений за різною віртуальною адресою. DLL, безумовно, підтримує це.
dma_k

20
@dma_k: DLL-файли Windows можна завантажувати за різними адресами, але лише тому, що лінкер копіює весь код та змінює адресні номери. У спільних об'єктах всі посилання на адресу відносні, тому кілька процесів можуть спільно використовувати одну пам'ять для спільного об'єкта. Іншими словами, у Windows, DLL розміром 1 Мб, що використовується 3 програмами = 3 Мб. У Linux, МБ SO, що використовується 3 програмами = 1 Мб.
Зіфре

7
І Windows, і Linux мають концепцію перенесення часу на завантаження спільних бібліотек eli.thegreenplace.net/2011/08/25/… Найбільше, що дозволило незалежному коду позиції, не було чимось особливим для Linux, а додано адресацію, пов'язану з RIP. з набором інструкцій x64; і Windows, і Linux можуть використовувати відносну RIP-адресування, зменшуючи кількість виправлень при переміщенні бібліотек.
clemahieu

194

Інші належним чином пояснили, що таке статична бібліотека, але я хотів би зазначити деякі застереження щодо використання статичних бібліотек, принаймні для Windows:

  • Сінглтон: Якщо щось має бути глобальним / статичним та унікальним, будьте дуже обережні, поклавши його в статичну бібліотеку. Якщо кілька статичних бібліотек пов'язані з цією статичною бібліотекою, вони отримають власну копію сингтона. Однак якщо у вашій програмі є один 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(), ви можете отримати небажані та дивовижні результати.


23
Щодо суті про одиночні кнопки, не забувайте, що DLL може завантажуватися декілька разів (однакова версія або версії mulitple), і досі немає гарантії одиночної форми.
Оріон Адріан

Додатковий пункт про видалення невпорядкованого коду: Виклики, зроблені в DLL, також вимагають фактичного виклику, щоб змусити завантажувати посилання DLL. Якщо додати його як посилання, але потім не включаючи жодного дзвінка, на який посилається, все одно ви отримаєте такий же результат, як і статична бібліотека, яка нічого не викликає. Різниця лише в тому, що насправді поставляється. В обох випадках статичні конструктори та деструктори не спрацьовують.
Оріон Адріан

@ bk1e Це не повинно статися. .a завжди буде містити всі символи, з якими він був побудований. Коли він статично пов'язаний з вашою програмою, так, лише ті символи, які використовуються, будуть пов'язані.
Miles Rout

62

Lib - це одиниця коду, яка входить у виконуваний файл програми.

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

Dll плюси : можна використовувати для повторного використання / спільного використання коду між декількома продуктами; завантаження в оперативну пам’ять на вимогу і може бути завантажено, коли не потрібно; можна оновити незалежно від решти програми.

Мінуси Dll : вплив на ефективність завантаження dll та скидання коду; проблеми з версією ("dll hell")

Професіонали Lib : відсутність впливу на продуктивність, оскільки код завжди завантажується в процесі і не перезавантажується; немає проблем з версією.

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


Повторне звільнення можна також здійснити під час збирання за допомогою rebase.exe або шляхом передачі параметра / BASE на link.exe. Наскільки це ефективно, залежить від того, чи є несподівані конфлікти адресного простору під час виконання.
bk1e

24

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

Наприклад, якщо ви користуєтесь ліцензійним кодом LGPL і стаціонарно зв’язуєтесь із бібліотекою LGPL (і таким чином створюєте одну велику бінарну), ваш код автоматично стає відкритим Sourced ( безкоштовним, як у свободі) кодом LGPL. Якщо ви посилаєтесь на спільні об'єкти, вам потрібно лише LGPL покращити / виправити помилки, які ви вносите до самої бібліотеки LGPL.

Це стає набагато важливішим питанням, якщо ви вирішуєте, як скласти мобільні додатки, наприклад (в Android у вас є вибір статичної та динамічної, в iOS ви цього не робите - це завжди статична).


23

Програми C ++ будуються у два етапи

  1. Компіляція - створює об'єктний код (.obj)
  2. Посилання - створює виконуваний код (.exe або .dll)

Статична бібліотека (.lib) - це лише група файлів .obj, тому не є повною програмою. Він не пройшов другий (пов'язуючий) етап створення програми. Dlls, з іншого боку, схожі на exe, і тому є повноцінними програмами.

Якщо ви будуєте статичну бібліотеку, вона ще не пов'язана, і тому споживачам вашої статичної бібліотеки доведеться використовувати той самий компілятор, який ви використовували (якщо ви використовували g ++, вони повинні використовувати g ++).

Якщо замість цього ви створили dll (і правильно створили його ), ви створили повну програму, яку можуть використовувати всі споживачі, незалежно від того, який компілятор вони використовують. Існує кілька обмежень щодо експорту з DLL, якщо бажана сумісність між компілятором.


1
Це для мене новина. Які обмеження існують у крос-компіляторів при використанні DLL-файлів? Побудова програміста без необхідності того ж ланцюжка інструментів здається величезним плюсом для DLL
Dan

1
Ця відповідь інформативна. Додавання мінорного застереження: consumers of your static library will have to use the same compiler that you usedякщо статична бібліотека використовує бібліотеку C ++, наприклад #include <iostream>.
truthadjustr

не можна споживати c ++ dll, якщо не використовується той самий компілятор (оскільки не існує стандартного c ++ abi, символи вручаються різними способами). І dll, і клієнтський модуль повинні використовувати один і той же компілятор і однакові налаштування збірки
kcris

19

Створення статичної бібліотеки

$$:~/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]>

13

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

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


13

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

Якщо є два додатки, які використовують спільний код, чи бажаєте ви змусити ці програми змінюватися разом, якщо вони повинні бути сумісні один з одним? Потім використовуйте dll. Усі exe будуть використовувати один і той же код.

Або ви хочете ізолювати їх один від одного, щоб ви могли змінити одне і бути впевненим, що ви не зламали іншого. Потім використовуйте статичну вкладку.

Пекло DLL - це, коли ви, ймовірно, ДОЛЖЕНЕ ВИКОРИСТАТИ статичну лібу, але замість цього ви використовували dll, і не всі exe можна з цим поєднати.


9

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

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

Ви будете використовувати статичну бібліотеку, коли у вас немає причин використовувати динамічну бібліотеку.


Ви також можете використовувати DLL, коли багато інших додатків використовують однакові функції - це може зменшити кількість слідів.
Тім

Крім того, розширення вашої початкової концепції, архітектури "плагінів", де ви хочете дозволити додані / невідомі функціонування пізніше, не потребуючи перебудови чи повторного випуску, можна зробити лише за допомогою динамічних бібліотек.
Тім

8

Документ Ульріха Дреппера про те, як записати спільні бібліотеки , також є хорошим ресурсом, який детально описує, як найкраще скористатися спільними бібліотеками, або те, що він називає "Динамічними спільними об'єктами" (DSO). Він більше зосереджується на спільних бібліотеках у двійковому форматі ELF , але деякі дискусії також підходять для Windows DLL.


5

Щоб чудово обговорити цю тему, прочитайте цю статтю від Sun.

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


4

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


3

Якщо ваша бібліотека поділятиметься між кількома виконуваними файлами, часто має сенс зробити її динамічною для зменшення розміру виконуваних файлів. В іншому випадку обов'язково зробіть це статичним.

Є кілька недоліків використання dll. Для завантаження та вивантаження є додаткові накладні витрати. Існує також додаткова залежність. Якщо ви зміните dll, щоб зробити його несумісним з вашими виконавцями, вони перестануть працювати. З іншого боку, якщо ви зміните статичну бібліотеку, ваші компільовані виконувані файли за допомогою старої версії не вплинуть.


3

Якщо бібліотека статична, то під час посилання код пов'язується з вашим виконуваним файлом. Це робить ваш виконуваний файл більшим (ніж якщо б ви пройшли динамічний маршрут).

Якщо бібліотека динамічна, то під час посилання до вашого виконуваного файлу вбудовуються посилання на необхідні методи. Це означає, що вам потрібно доставити виконуваний файл і динамічну бібліотеку. Вам також слід розглянути питання про те, чи безпечний спільний доступ до коду в бібліотеці безпечним, бажана адреса завантаження серед інших матеріалів.

Якщо ви можете жити зі статичною бібліотекою, перейдіть до статичної бібліотеки.


3

У нашому проекті ми використовуємо багато 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 повинні бути присутніми в остаточному об'єднаному виконуваному файлі лише один раз. Хтось може підтримати це питання?


Чи не ви хотіли якимось чином використовувати деякі директиви перед компілятором, щоб уникнути дублювання коду?
Пейсман

Попередня компіляція Afaiac працює лише на базі кожного модуля (exe / dll / lib). Попереднє компілювання призначене насамперед для прискорення компіляції, хоча воно також запобігає множинним включенням всередині компіляційного блоку. Однак кращі способи досягти цього ефекту включають охоронці.
gast128

2

Статичні бібліотеки - це архіви, що містять об'єктний код для бібліотеки, коли вони приєднані до програми, що код збирається у виконуваний файл. Спільні бібліотеки відрізняються тим, що вони не збираються у виконуваний файл. Натомість динамічний лінкер здійснює пошук у деяких каталогах, які шукають потрібну йому бібліотеку, а потім завантажує його в пам'ять. Більше одного виконуваного файлу може одночасно використовувати одну і ту ж спільну бібліотеку, зменшуючи використання пам'яті та розмір виконуваного файлу. Однак є більше файлів, які можна розповсюдити за допомогою виконуваного файлу. Вам потрібно переконатися, що бібліотека встановлена ​​в системі використання десь, де лінкер може її знайти, статичне посилання усуває цю проблему, але призводить до отримання більшого виконуваного файлу.


2

Якщо ваша робота над вбудованими проектами або на спеціалізованих платформах є єдиним способом статичних бібліотек, також багато разів вони менше клопоту збирати у вашій програмі. Також наявність проектів та файлів, що включають все, робить життя щасливішим.


2

Я б сказав загальне правило: якщо у вас є велика база коду, всі вбудовані на вершині бібліотеки нижчого рівня (наприклад, Utils або Gui Framework), які ви хочете розділити на більш керовані бібліотеки, а потім зробити їх статичними бібліотеками. Динамічні бібліотеки насправді нічого не купують, і сюрпризів менше - буде, наприклад, лише один екземпляр одинаків.

Якщо у вас є бібліотека, яка повністю відокремлена від решти кодової бази (наприклад, стороння бібліотека), тоді подумайте про те, щоб зробити її dll. Якщо бібліотека є LGPL, можливо, вам доведеться все-таки використовувати dll через ліцензійні умови.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.