Разница между общими объектами (.so), статическими библиотеками (.a) и DLL (.so)?

Я участвовал в некоторых дебатах по поводу библиотек в Linux и хотел бы подтвердить некоторые вещи.

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

  1. Статические библиотеки (файлы .a): во время компоновки копия всей библиотеки помещается в конечное приложение, чтобы функции внутри библиотеки всегда были доступны вызывающему приложению
  2. Общие объекты (файлы .so). Во время соединения объект просто проверяется на соответствие его API через соответствующий файл заголовка (.h). Библиотека фактически не используется до времени выполнения, где это необходимо.

Очевидное преимущество статических библиотек заключается в том, что они позволяют полностью автономно использовать все приложение, тогда как преимущество динамических библиотек заключается в том, что файл ".so" можно заменить (т.е. в случае необходимости его обновления из-за безопасности. ошибка) без необходимости перекомпиляции базового приложения.

Я слышал, что некоторые люди делают различие между общими объектами и динамически связанными библиотеками (DLL), хотя они оба являются файлами ".so". Есть ли какое-либо различие между общими объектами и библиотеками DLL, когда речь заходит о разработке C/C++ в Linux или любой другой POSIX-совместимой ОС (например, MINIX, UNIX, QNX и т.д.)? Мне говорят, что одно ключевое отличие (до сих пор) заключается в том, что общие объекты просто используются во время выполнения, в то время как DLL необходимо сначала открыть с помощью вызова dlopen() в приложении.

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

Заранее благодарю всех за помощь.

Обновление


В контексте, в котором эти термины были предоставлены мне, это были фактически ошибочные термины, используемые командой разработчиков Windows, которые должны были изучать Linux. Я пытался их исправить, но (неправильные) языковые нормы застряли.

  1. Общий объект: библиотека, которая автоматически связывается с программой при запуске и существует как отдельный файл. Библиотека включается в список ссылок во время компиляции (то есть: LDOPTS+=-lmylib для файла библиотеки с именем mylib.so). Библиотека должна присутствовать во время компиляции и при запуске приложения.
  2. Статическая библиотека: библиотека, которая объединяется с самой программой во время сборки для одного (большего) приложения, содержащего код приложения и код библиотеки, который автоматически связывается с программой при сборке программы, и конечный двоичный файл, содержащий оба основная программа и сама библиотека существуют в виде отдельного двоичного файла. Библиотека включается в список ссылок во время компиляции (то есть: LDOPTS+=-lmylib для файла библиотеки с именем mylib.a). Библиотека должна присутствовать во время компиляции.
  3. DLL: по сути то же самое, что и общий объект, но вместо того, чтобы быть включенным в список ссылок во время компиляции, библиотека загружается с помощью команд dlopen()/dlsym(), так что библиотека не должна присутствовать во время сборки для программа для компиляции. Кроме того, библиотека не обязательно должна присутствовать (обязательно) при запуске приложения или во время компиляции, поскольку она необходима только в момент выполнения вызовов dlopen/dlsym.
  4. Общий архив: по сути, такой же, как статическая библиотека, но компилируется с флагами "export-shared" и "-fPIC". Библиотека включается в список ссылок во время компиляции (то есть: LDOPTS+=-lmylibS для файла библиотеки с именем mylibS.a). Различие между ними заключается в том, что этот дополнительный флаг необходим, если общий объект или DLL хотят статически связать общий архив с собственным кодом и иметь возможность сделать функции в общем объекте доступными для других программ, а не просто использовать их. внутренний в DLL. Это полезно в случае, когда кто-то предоставляет вам статическую библиотеку, и вы хотите переупаковать ее как SO. Библиотека должна присутствовать во время компиляции.

Дополнительное обновление

Различие между "DLL" и "shared library" было просто (ленивым, неточным) коллоквиализмом в компании, в которой я работал в то время (разработчики Windows были вынуждены перейти к разработке Linux, и термин застрял), придерживаясь к описаниям, указанным выше.

Кроме того, конечный литерал "S" после имени библиотеки, в случае "общих архивов", был просто соглашением, используемым в этой компании, а не в отрасли в целом.

Ответ 1

Я всегда думал, что DLL и общие объекты - это просто разные термины для одного и того же - Windows называет их DLL, в то время как в UNIX-системах они являются общими объектами, причем общий термин - динамически связанная библиотека - охватывает как (даже функция для открытия .so в UNIX называется dlopen() после "динамической библиотеки" ).

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

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

Ответ 2

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

A общая библиотека (.so) - это библиотека, которая связана, но не внедрена в окончательный исполняемый файл, поэтому будет загружаться при запуске исполняемого файла и должна присутствовать в системе, исполняемый файл.

Динамическая библиотека ссылок на Windows (.dll) похожа на общую библиотеку (.so) на linux, но есть некоторые различия между двумя реализациями, связанными с ОС (Windows vs Linux ):

A DLL может определять два вида функций: экспортируемый и внутренний. Экспортируемые функции предназначены для вызова другими модулями, а также из библиотеки DLL, где они определены. Внутренние функции обычно предназначены для вызова только из библиотеки DLL, где они определены.

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

Ответ 3

Я могу подробно рассказать о DLL файлах в Windows, чтобы помочь прояснить эти тайны моим друзьям здесь, в NIX-land...

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

У библиотек Windows есть таблица экспорта. Экспорт может быть указан по имени или по таблице (числовой). Последний метод считается "старой школой" и гораздо более хрупким - перестройка DLL и изменение позиции функции в таблице закончится катастрофой, в то время как нет реальной проблемы, если связывание точек входа по имени. Итак, забудьте об этом как о проблеме, но просто имейте это в виду, если вы работаете с кодом "динозавр", например, сторонними поставщиками libs.

Библиотеки Windows создаются путем компиляции и компоновки, как и для EXE (исполняемого приложения), но DLL предназначена не для одиночки, как SO, предназначенная для использования приложением, либо через динамическое загрузка или привязка времени привязки (ссылка на SO встроена в бинарные метаданные приложения, а загрузчик программ ОС будет автоматически загружать ссылки SO). DLL могут ссылаться на другие DLL, так же как SO могут ссылаться на другие SO.

В Windows, библиотеки DLL будут предоставлять только определенные точки входа. Они называются "экспорт". Разработчик может либо использовать специальное ключевое слово компилятора, чтобы сделать символ видимым извне (другим компоновщикам и динамическим загрузчиком), либо экспорт может быть указан в файле определения модуля, который используется во время соединения, когда сама DLL создается. Современная практика заключается в том, чтобы украсить определение функции ключевым словом для экспорта имени символа. Также возможно создавать файлы заголовков с ключевыми словами, которые будут объявлять этот символ как один, который будет импортирован из DLL за пределами текущего модуля компиляции. Для получения дополнительной информации найдите ключевые слова __declspec (dllexport) и __declspec (dllimport).

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

Когда разработчик хочет использовать уже построенную DLL, она должна либо ссылаться на "библиотеку экспорта" (*.LIB), созданную разработчиком DLL, когда она создавала DLL, либо она должна явно загружать DLL во время выполнения и запросить адрес точки входа по имени через механизмы LoadLibrary() и GetProcAddress(). В большинстве случаев ссылка на файл LIB (который просто содержит метаданные компоновщика для экспортированных точек входа DLL) - это способ использования DLL. Обычно динамическая загрузка зарезервирована для реализации "полиморфизма" или "конфигурации времени выполнения" в программном поведении (доступ к надстройкам или более поздним функциям, также называемым "плагинами" ).

Путь к Windows может привести к некоторой путанице; система использует расширение .LIB для обозначения как обычных статических библиотек (архивов, таких как файлы POSIX *.a), так и библиотек "export stub", необходимых для привязки приложения к DLL во время ссылки. Поэтому всегда нужно посмотреть, имеет ли файл *.LIB файл с одинаковым именем *.DLL; если нет, то возможно, что файл *.LIB является архивом статической библиотеки, а не экспортирует метаданные связывания для DLL.

Ответ 4

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

Вызов dlopen предназначен не только для общих объектов, если приложение хочет сделать это во время выполнения от своего имени, в противном случае общие объекты загружаются автоматически при запуске приложения. DLLS и .so - одно и то же. dlopen существует, чтобы добавить еще более мелкозернистые возможности динамической загрузки для процессов. Вам не нужно использовать dlopen самостоятельно, чтобы открывать/использовать библиотеки DLL, что также происходит при запуске приложения.