Что означают «статически связанные» и «динамически связанные»? - PullRequest
203 голосов
/ 23 ноября 2008

Я часто слышу термины «статически связанные» и «динамически связанные», часто в отношении кода, написанного на C , C ++ или C # . О чем они, что именно они говорят и что они связывают?

Ответы [ 5 ]

403 голосов
/ 23 ноября 2008

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

Первый - это компиляция, которая превращает исходный код в объектные модули.

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

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

210 голосов
/ 23 ноября 2008

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

Когда вы компилируете некоторый код C (например), он переводится на машинный язык. Просто последовательность байтов, которая при запуске заставляет процессор складывать, вычитать, сравнивать, «переходить», читать память, записывать память и тому подобное. Этот материал хранится в объектных (.o) файлах.

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

Теперь в первые дни программистам приходилось указывать адрес памяти, на котором были расположены эти подпрограммы. Что-то вроде CALL 0x5A62. Это было утомительно и проблематично, если эти адреса памяти нужно когда-либо менять.

Итак, процесс был автоматизирован. Вы пишете программу, которая вызывает printf(), а компилятор не знает адрес памяти printf. Таким образом, компилятор просто пишет CALL 0x0000 и добавляет к объектному файлу примечание о том, что «необходимо заменить 0x0000 в ячейке памяти printf ».

Статическая связь означает, что программа компоновщика (GNU-программа называется ld ) добавляет машинный код printf непосредственно в ваш исполняемый файл и заменяет 0x0000 на адрес printf. Это происходит при создании вашего исполняемого файла.

Динамическое связывание означает, что вышеуказанный шаг не выполняется. В исполняемом файле still есть примечание, в котором говорится "необходимо заменить 0x000 на место в памяти printf". Загрузчик операционной системы должен найти код printf, загрузить его в память и исправить адрес CALL, при каждом запуске программы .

Обычно программы вызывают некоторые функции, которые будут статически связаны (стандартные функции библиотеки, такие как printf, как правило, статически связаны) и другие функции, которые динамически связаны. Статические «становятся частью» исполняемого файла, а динамические «включаются» при запуске исполняемого файла.

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

29 голосов
/ 23 ноября 2008

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

12 голосов
/ 06 октября 2015

Потому что ни один из вышеперечисленных постов на самом деле не показывает, как статически связать что-то и увидеть, что вы сделали это правильно, поэтому я решу эту проблему:

Простая программа на С

#include <stdio.h>

int main(void)
{
    printf("This is a string\n");
    return 0;
}

Динамически связать программу C

gcc simpleprog.c -o simpleprog

И запустить file в двоичном файле:

file simpleprog 

И это покажет, что оно динамически связано с чем-то вроде:

"simpleprog: исполняемый ELF 64-битный LSB, x86-64, версия 1 (SYSV), динамически связанный (использует разделяемые библиотеки), для GNU / Linux 2.6.26, BuildID [sha1] = 0xf715572611a8b04f686809d90d1c0d75c6028f0f, не удален"

Вместо этого давайте на этот раз статически свяжем программу:

gcc simpleprog.c -static -o simpleprog

Запуск файла в этом статически связанном двоичном файле покажет:

file simpleprog 

"simpleprog: исполняемый 64-разрядный LSB ELF, x86-64, версия 1 (GNU / Linux), статически связанная, для GNU / Linux 2.6.26, BuildID [sha1] = 0x8c0b12250801c5a7c7434647b7dc65a644d6132b, без зачистки"

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

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

Теперь strace двоичный файл, который вы создали, и вы можете видеть, что нет никаких библиотек, к которым обращались до запуска программы:

strace ./simpleprog

Теперь сравните с выводом strace для динамически связанной программы, и вы увидите, что strace в статически связанной версии намного короче!

2 голосов
/ 23 ноября 2008

(я не знаю C #, но интересно иметь статическую концепцию связывания для языка ВМ)

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

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

...