Подтвердить что ты не робот

Файлы объектов и файлы библиотеки и почему?

Я понимаю основы компиляции. Исходные файлы, скомпилированные в объектные файлы, которые компоновщик затем связывает с исполняемыми файлами. Эти объектные файлы состоят из исходных файлов, содержащих определения.

Итак, мои вопросы:


  • Почему у нас есть отдельная реализация для библиотеки?.a.lib, .dll...
  • Я, наверное, ошибаюсь, но мне кажется, что файлы .o сами являются похожими на библиотеки?
  • Не могли бы вы дать вам свои .o реализации определенного (.h), и вы можете заменить это, и связать его с становится исполняемым, который выполняет те же функции, но используя разные операции?
4b9b3361

Ответ 1

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

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

Чтобы повысить производительность (загрузка целой библиотеки занимает некоторое время, особенно с медленных носителей, таких как гибкие диски), библиотеки часто содержат индекс, который сообщает компоновщику, какие объектные файлы предоставляют какие символы. Индексы создаются такими инструментами, как ranlib или инструментом управления библиотекой (Borland tlib имеет переключатель для создания индекса). Как только появляется индекс, библиотеки, безусловно, более эффективны для связывания однотипных файлов объектов, даже если все объектные файлы находятся в кеше диска и загрузка файлов из кеша диска бесплатна.

Вы совершенно правы, что я могу заменить файлы .o или .a, сохраняя файлы заголовков и изменяя то, что делают функции (или как они это делают). Это используется лицензией LPGL, которая требует от автора программы, использующей библиотеку с лицензией LPGL, чтобы дать пользователю возможность заменить эту библиотеку исправленной, улучшенной или альтернативной реализацией. Доставка объектных файлов собственного приложения (возможно, сгруппированных в виде файлов библиотеки) достаточно, чтобы предоставить пользователю необходимую свободу; нет необходимости отправлять исходный код (например, с помощью GPL).

Если два набора библиотек (или объектных файлов) могут быть успешно использованы с одинаковыми файлами заголовков, они, как говорят, совместимы с ABI, где ABI означает Application Binary Interface. Это более узко, чем просто наличие двух наборов библиотек (или объектных файлов), сопровождаемых их соответствующими заголовками, и гарантируя, что вы можете использовать каждую библиотеку, если используете заголовки для этой конкретной библиотеки. Это называется API-совместимостью, где API означает интерфейс прикладных программ. В качестве примера разницы рассмотрим следующие три файла заголовка:

Файл 1:

typedef struct {
    int a;
    int __undocumented_member;
    int b;
} magic_data;
magic_data* calculate(int);

Файл 2:

struct __tag_magic_data {
    int a;
    int __padding;
    int b;
};
typedef __tag_magic_data magic_data;
magic_data* calculate(const int);

Файл 3:

typedef struct {
    int a;
    int b;
    int c;
} magic_data;
magic_data* do_calculate(int, void*);
#define calculate(x) do_calculate(x, 0)

Первые два файла не идентичны, но они предоставляют обменные определения, которые (насколько я ожидаю) не нарушают "одно правило определения", поэтому библиотеку, предоставляющую файл 1 в качестве заголовочного файла, можно также использовать с файлом 2 в качестве файла заголовка. С другой стороны, File 3 обеспечивает очень похожий интерфейс с программистом (который может быть идентичным во всем, что автор библиотеки promises пользователь библиотеки), но код, скомпилированный с файлом 3, не может связываться с библиотекой, предназначенной для будет использоваться с файлом 1 или файлом 2, поскольку библиотека, предназначенная для файла 3, не будет экспортировать calculate, а только do_calculate. Кроме того, структура имеет разный макет элемента, поэтому использование файла 1 или файла 2 вместо файла 3 не будет корректно работать с b. Библиотеки, предоставляющие файлы 1 и 2, совместимы с ABI, но все три библиотеки совместимы с API (при условии, что c и более эффективная функция do_calculate не относятся к этому API).

Для динамических библиотек (.dll,.so) все совершенно по-другому: они начали появляться в тех системах, где одновременно могут загружаться несколько (прикладных) программ (что не так для DOS, но это так на Windows). Равноценно иметь одну и ту же реализацию библиотечной функции в памяти несколько раз, поэтому загрузка ее только один раз в память имеет различное применение, она сохраняет память. Для динамических библиотек код ссылочной функции не включен в исполняемый файл, но включена только ссылка на функцию внутри динамической библиотеки (для Windows NE/PE указано, какая DLL должна предоставить какую функцию; для файлов Unix.so указывается только имена функций и набор библиотек). Операционная система содержит загрузчик, который является динамическим компоновщиком, который решает эти ссылки и загружает динамические библиотеки, если они еще не находятся в памяти на момент запуска программы.

Ответ 2

Хорошо, начнем с начала.

Программист (вы) создает некоторые исходные файлы, .c++ и .h. Разница между этими двумя файлами - это просто соглашение:

  • .c++ должны быть скомпилированы
  • .h предназначены для включения в другие исходные файлы

но ничто (кроме страха иметь непримиримую вещь) запрещает вам импортировать файлы c++ в другие файлы .c++.

В начале C (предка С++) .h файл содержал только объявления функций, структур (без методов в C!) и констант. Вы также можете иметь макрос (#define), но кроме того, что код не должен находиться в .h.

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

Теперь ответ на ваш вопрос:

Каждый .c++ файл является компилятором. Компилятор будет:

  • в фазе процесса препроцессора все #include или #define в (внутренне) генерирует полный исходный код
  • компилирует его в формат объекта (обычно .o или .obj)

Этот формат объекта содержит:

  • перемещаемый код (то есть адреса в коде или переменные являются родственниками экспортированных символов)
  • экспортированные символы: символы, которые могут использоваться из других единиц компиляции (функции, классы, глобальные переменные)
  • импортированные символы: символы, используемые в этом модуле компиляции и определенные в других единицах компиляции

Затем (пусть теперь забывают библиотеки) компоновщик объединяет все единицы компиляции и разрешает символы для создания исполняемого файла.

Еще один шаг со статическими библиотеками.

Статическая библиотека (обычно .a или .lib) - это более или менее совокупность объединенных объектных файлов. Он существует, чтобы не перечислить индивидуально каждый файл объекта, который вам нужен, те, из которых вы используете экспортированные символы. Связывание библиотеки, содержащей объектные файлы, которые вы используете, и связывание самих файлов объектов, одинаковы. Просто добавление -lc, -lm или -lx11 короче добавляет сотни файлов .o. Но, по крайней мере, в Unix-подобных системах статическая библиотека представляет собой архив, и вы можете извлечь отдельные файлы объектов, если хотите.

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

А теперь посмотрим на включенные файлы. Ни старый добрый K & R C, ни самый последний С++ не имеют понятия глобального модуля для импорта, например Java или С#. В этих языках при импорте модуля вы получаете как декларации для своих экспортированных символов, так и указание на то, что вы позже свяжете его. Но в С++ (то же самое в C) вы должны сделать это отдельно:

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

Ответ 3

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

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

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

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

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

Существует фактически (по крайней мере) два типа библиотеки.

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

Динамически связанные библиотеки загружаются в программу во время выполнения. Два преимущества заключаются в том, что исполняемый файл меньше (поскольку он не содержит содержимого объектных файлов или статических библиотек), и что несколько исполняемых файлов могут использовать каждую динамически связанную библиотеку (т.е. Необходимо только распространять/устанавливать библиотеки один раз, и все исполняемые файлы, которые используют эти библиотеки, будут работать). Смещение этого означает, что установка программ становится более сложной (исполняемые файлы не будут выполняться, если динамически связанные библиотеки не могут быть найдены, поэтому процессы установки должны справляться с потенциальной необходимостью установки библиотек хотя бы один раз). Еще одно преимущество заключается в том, что динамические библиотеки могут быть обновлены без необходимости изменения исполняемого файла - например, для устранения недостатка в одной из функций, содержащихся в библиотеке, и, следовательно, для исправления функционирования всех программ, которые используют эту библиотеку, без изменения исполняемых файлов, Смещение этого состоит в том, что программа, которая опирается на недавнюю версию библиотеки, может работать неправильно, если будет найдена только старая версия библиотеки при ее запуске. Это дает проблемы обслуживания с библиотеками (называемыми различными именами, такими как аддон DLL), особенно когда программы полагаются на несколько динамически связанных библиотек. Примеры динамически связанных библиотек включают библиотеки DLL под окнами,.so файлы под Unix. Средства, предоставляемые операционными системами, часто устанавливаются - с операционной системой - в виде динамически связанных библиотек, что позволяет всем программам (при правильном построении) использовать службы операционной системы.

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

Ответ 4

То, что вы описываете, - это то, как работает статическая привязка.

Почему у нас есть отдельная реализация для библиотеки?.a.lib,.dll...

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

.so - одна и та же идея, но в Linux.

.a s, традиционно используемые в Linux (а также в MinGW), являются библиотечными архивами, которые в основном ведут себя как расширенные объектные файлы:

  • они связаны статически.
  • вы можете упаковать несколько объектных файлов в архив одной библиотеки.
  • имена индексируются.

.lib используются компоновщиком Microsoft в Visual Studio.

Не могли бы вы дать вам свои .o реализации определенного объявления (.h), и вы могли бы заменить его и связать ли он как исполняемый файл, который выполняет те же функции, но используя разные операции?

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

Практический пример - Wine - они обеспечивают открытую и переносимую реализацию WinAPI.