Відповіді:
Існує (у більшості випадків дисконтування інтерпретованого коду) два етапи переходу від вихідного коду (те, що ви пишете) до виконуваного коду (що ви запускаєте).
Перша - компіляція, яка перетворює вихідний код в об'єктні модулі.
Друге, що пов'язує, - це те, що поєднує об'єктивні модулі разом, щоб утворювати виконуваний файл.
Відмінність полягає в тому, що, крім іншого, дозволяють сторонні бібліотеки включати у свій виконуваний файл, не бачачи їх вихідного коду (наприклад, бібліотеки для доступу до бази даних, мережевих комунікацій та графічних інтерфейсів користувача) або для складання коду на різних мовах ( C і код складання, наприклад), а потім з'єднавши їх усі разом.
Коли ви статично зв'язуєте файл у виконуваний файл, його вміст включається під час посилання. Іншими словами, вміст файла фізично вставляється у виконуваний файл, який ви будете запускати.
Коли ви динамічно посилаєтесь , покажчик на файл, до якого пов'язаний (наприклад, ім'я файлу), включається у виконуваний файл, а вміст цього файлу не включається під час посилання. Лише тоді, коли ви запускаєте виконуваний файл, ці динамічно пов'язані файли купуються, і вони купуються лише в пам'ять копії виконуваного файлу, а не на диску.
Це в основному метод відкладеного зв'язку. Існує ще більш відкладений метод (який називається пізньою прив'язкою для деяких систем), який не введе динамічно пов'язаний файл, поки ви фактично не спробуєте викликати функцію всередині нього.
Статично пов'язані файли 'заблоковані' у виконуваний файл під час посилання, тому вони ніколи не змінюються. Динамічно пов'язаний файл, на який посилається виконуваний файл, може змінитись лише заміною файла на диску.
Це дозволяє оновлювати функціональність без необхідності повторного посилання коду; завантажувач повторно посилається кожного разу, коли ви його запускаєте.
Це і добре, і погано - з одного боку, це дозволяє більш легкі оновлення та виправлення помилок, з іншого - це може призвести до припинення роботи програм, якщо оновлення несумісні - це іноді є причиною жахливого "пекла DLL", який деякі люди зауважте, що програми можуть бути зламані, якщо замінити динамічно пов'язану бібліотеку на ту, яка не сумісна (розробники, які це роблять, повинні сподіватися, що вони будуть покладені на хутір і суворо покарані).
Як приклад , давайте розглянемо випадок, коли користувач збирає свій main.c
файл для статичного та динамічного посилання.
Phase Static Dynamic
-------- ---------------------- ------------------------
+---------+ +---------+
| main.c | | main.c |
+---------+ +---------+
Compile........|.........................|...................
+---------+ +---------+ +---------+ +--------+
| main.o | | crtlib | | main.o | | crtimp |
+---------+ +---------+ +---------+ +--------+
Link...........|..........|..............|...........|.......
| | +-----------+
| | |
+---------+ | +---------+ +--------+
| main |-----+ | main | | crtdll |
+---------+ +---------+ +--------+
Load/Run.......|.........................|..........|........
+---------+ +---------+ |
| main in | | main in |-----+
| memory | | memory |
+---------+ +---------+
У статичному випадку ви бачите, що основна програма та бібліотека виконання C пов'язані між собою у час зв'язку (розробниками). Оскільки користувач, як правило, не може повторно зв’язати виконуваний файл, він застряг у поведінці бібліотеки.
У динамічному випадку основна програма пов’язана з бібліотекою імпорту програми C (що декларує те, що є в динамічній бібліотеці, але насправді не визначає ). Це дозволяє лінкеру зв’язатись, хоча фактичний код відсутній.
Тоді, під час виконання, завантажувач операційної системи здійснює пізнє з'єднання основної програми з DLL виконання програми C (динамічна бібліотека посилань або спільна бібліотека чи інша номенклатура).
Власник програми C може в будь-який час скинути нову DLL, щоб забезпечити оновлення або виправлення помилок. Як було сказано раніше, це має як переваги, так і недоліки.
.dll
або .so
розширення) - думайте про відповідь як пояснення понять, а не як точний опис. І, відповідно до тексту, це приклад, що показує статичне та динамічне посилання лише на файли часу виконання C, так що так, це `crt вказує у всіх них.
Я думаю, що хороший відповідь на це питання має пояснити , що зв'язування є .
Коли ви компілюєте якийсь код C (наприклад), він перекладається на машинну мову. Просто послідовність байтів, яка під час запуску змушує процесор додавати, віднімати, порівнювати, "goto", читати пам'ять, записувати пам'ять, подібні речі. Цей матеріал зберігається в об’єктних (.o) файлах.
Зараз, давно, комп'ютерні вчені винайшли цю "підпрограму". Виконайте тут-ось-цю частину коду-і поверніть сюди. Не так давно вони зрозуміли, що найкорисніші підпрограми можуть зберігатися в спеціальному місці та використовуватися будь-якою програмою, яка їм потрібна.
Тепер у перші дні програмістам доведеться пробивати адресу пам'яті, на якій знаходились ці підпрограми. Щось подібне CALL 0x5A62
. Це було нудно і проблематично, якщо ці адреси пам’яті колись потрібно змінювати.
Отже, процес був автоматизований. Ви пишете програму, яка дзвонить printf()
, і компілятор не знає адреси пам'яті printf
. Тож компілятор просто пише CALL 0x0000
і додає примітку до об’єктного файлу, що говорить "повинен замінити цей 0x0000 місцем пам'яті printf ".
Статичний зв'язок означає, що програма лінкера (GNU називається ld ) додає printf
машинний код безпосередньо у ваш виконуваний файл і змінює 0x0000 на адресу printf
. Це відбувається, коли створений ваш виконуваний файл.
Динамічна зв'язок означає, що вищезазначений крок не відбудеться. У виконаному файлі все ще є примітка, в якій сказано, що "повинен замінити 0x000 місцем пам'яті printf". Завантажувач операційної системи повинен знайти код printf, завантажити його в пам'ять і виправити адресу CALL щоразу, коли програма запускається .
Програми звичайно викликають деякі функції, які будуть статично пов'язані (стандартні функції бібліотеки, як printf
правило, статично пов'язані) та інші функції, які динамічно пов'язані. Статичні "стають частиною" виконуваного файлу, а динамічні "приєднуються" під час запуску виконуваного файлу.
У обох методів є переваги та недоліки, а між операційними системами є відмінності. Але оскільки ви не запитували, я закінчу це на цьому.
ld
документацію GNU .
Статистично пов’язані бібліотеки пов'язані в час компіляції. Динамічно пов'язані бібліотеки завантажуються під час виконання. Статичне з'єднання перетворює біт бібліотеки у ваш виконуваний файл. Динамічне посилання пов'язане лише з посиланням на бібліотеку; біти динамічної бібліотеки існують в іншому місці і можуть бути замінені пізніше.
Оскільки жоден з перерахованих вище публікацій насправді не показує, як статично щось зв’язати і побачити, що ви це зробили правильно, тому я вирішу це питання:
Проста програма C
#include <stdio.h>
int main(void)
{
printf("This is a string\n");
return 0;
}
Динамічно зв’язуйте програму С
gcc simpleprog.c -o simpleprog
І запустіть file
на бінарному:
file simpleprog
І це покаже, що це динамічно пов'язане щось за рубежем:
"simpleprog: 64-бітний виконуваний файл LSB ELF, x86-64, версія 1 (SYSV), динамічно пов'язаний (використовує спільні libs), для GNU / Linux 2.6.26, BuildID [sha1] = 0xf715572611a8b04f686809d90d1c0d75c6028f0f, не позбавлений"
Натомість давайте на цей раз статично зв’яжемо програму:
gcc simpleprog.c -static -o simpleprog
Запуск файлу на цьому статично пов'язаному бінарному файлі покаже:
file simpleprog
"simpleprog: 64-розрядний виконуваний файл LSB, x86-64, версія 1 (GNU / Linux), статично пов'язаний, для GNU / Linux 2.6.26, BuildID [sha1] = 0x8c0b12250801c5a7c7434647b7dc65a644d6132b, не позбавлений"
І ви можете бачити, що це щасливо статично пов'язане. На жаль, але не всі бібліотеки просто статично пов'язати таким чином, і це може зажадати великих зусиль, використовуючи libtool
або вручну зв’язуючи об'єктний код і бібліотеки С.
На щастя, багато вбудованих бібліотек C, як-от, musl
пропонують статичні параметри зв'язку майже для всіх, якщо не всіх їхніх бібліотек.
Тепер strace
створений вами бінарний файл ви бачите, що до запуску програми немає доступу до бібліотек:
strace ./simpleprog
Тепер порівняйте з результатами програми, strace
що динамічно пов'язана, і ви побачите, що статично пов'язана напруга версії набагато коротша!
(Я не знаю C #, але цікаво мати статичну концепцію зв'язку для мови VM)
Динамічне посилання передбачає знання потрібного функціоналу, на який у вас є лише посилання у вашій програмі. Ви мовно виконуєте час або ОС шукаєте фрагмент коду у файловій системі, мережі чи скомпільованому кеш-коді, узгоджуючи посилання, а потім вживаєте декількох заходів для інтеграції його до свого програмного зображення в пам'яті, наприклад, переїзд. Всі вони робляться під час виконання. Це можна зробити вручну або компілятором. Є можливість оновлення з ризиком зіпсувати (а саме DLL пекло).
Статичне пов'язування робиться під час компіляції, то ви повідомляєте компілятору, де є всі функціональні частини, і доручаєте їм їх інтегрувати. Немає ніякого пошуку, неясності, можливості оновлення без перекомпіляції. Всі ваші залежності фізично однакові із зображенням програми.