MKSHLIB(1)

НАЗВАНИЕ
mkshlib - создание разделяемой библиотеки

СИНТАКСИС

Mkshlib -s файл_спецификаций [-t библ_выполнения] [-h библ_сборки] [-n] [-L каталог] [-q]

ОПИСАНИЕ
Команда mkshlib создает как разделяемые библиотеки выполнения, так и разделяемые библиотеки сборки. Разделяемая библиотека очень похожа на обычную, неразделяемую об ектную библиотеку, но отличается тем, что программы, собранные с разделяемой библиотекой, будут во время выполнения пользоваться одним и тем же экземпляром секций команд библиотечных подпрограмм, тогда как в случае неразделяемых библиотек в каждом процессе имеется своя копия библиотечных подпрограмм.

Разделяемая библиотека сборки - это архив, который используется при редактировании связей между программами пользователя и разделяемой библиотекой [см. ar(4) ]. С разделяемой библиотекой сборки можно работать точно так же, как и с неразделяемой, она может обычным образом включаться в команду cc(1) . Кроме того, все операции, которые можно выполнять над архивом, могут быть выполнены и над разделяемой библиотекой сборки.

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

Интерфейс пользователя с командой mkshlib состоит из опций командной строки и файла спецификаций, описывающего содержимое разделяемой библиотеки.

Команда mkshlib производит вызов других программ, таких как ar(1) , as(1) , ld(1) . Обращение к другим программам происходит с помощью системного вызова execvp [см. exec(2) ], который ищет нужную программу в каталогах из списка поиска, хранящегося в переменной PATH. Команда mkshlib обрабатывает префиксы так же, как и команда cc(1) ; все программы, к которым производится обращение, получают тот же префикс. Например, pfxmkshlib вызовет pfxld.

Команда mkshlib распознает следующие опции: -s файл_спецификаций Указывает файл спецификаций разделяемой библиотеки. Этот файл содержит информацию, необходимую для построения разделяемой библиотеки. В нем находятся спецификации таблицы переходов для разделяемой библиотеки выполнения, маршрутное имя файла, в котором должна находиться разделяемая библиотека выполнения, начальные адреса секции команд и секции данных для разделяемой библиотеки выполнения, спецификации инициализации для разделяемой библиотеки сборки и список об ектных файлов, которые должны быть включены в разделяемую библиотеку (подробности см. ниже).

T библ_выполнения Указывает файл, в который будет помещена создаваемая разделяемая библиотека выполнения. Для того, чтобы воспользоваться разделяемой библиотекой выполнения, ее необходимо переместить в то место, которое указано в файле спецификации (см. ниже директиву #target). Если используется опция -n, генерации разделяемой библиотеки выполнения не производится.

H библ_сборки Указывает файл, в который будет помещена создаваемая разделяемая библиотека сборки. Если эта опция не указана, генерации разделяемой библиотеки сборки не производится.

N Не генерировать разделяемую библиотеку выполнения. Эта опция используется для того, чтобы создать только разделяемую библиотеку сборки. Тем не менее, следует обязательно указывать и опцию -t, так как для создания разделяемой библиотеки сборки требуется версия разделяемой библиотеки выполнения.

L каталог... Изменить алгоритм поиска для разделяемых библиотек сборки, указанных директивой #objects noload, так, что поиск вначале производится в указанном каталоге, а только потом в каталогах по умолчанию. Опция -L может быть указана в командной строке несколько раз, при этом каталоги просматриваются в указанном порядке перед поиском в каталогах по умолчанию.

Q Не выдавать предупреждающих сообщений. Эта опция используется, если известно, что будет выдано множество ненужных предупреждений.

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

Каждая директива, за которой может следовать несколько строк спецификаций, действует до тех пор, пока не закончится файл или не встретится другая директива. #address секция адрес Указывает начальный адрес секции разделяемой библиотеки выполнения. Эта директива обычно используется для задания начальных адресов секций.text и.data.

#target маршрутное_имя Задает маршрутное_имя разделяемой библиотеки выполнения во время ее использования. Указанное имя записывается в файлы типа a.out(4), обращающиеся к разделяемой библиотеке, и служит операционной системе для поиска разделяемой библиотеки, когда она требуется выполняемому файлу.

#branch Указывает на начало спецификаций таблицы переходов. Строки, следующие за этой директивой, трактуются как строки спецификации таблицы переходов. Строки спецификации таблицы переходов имеют следующий формат:

Имя_функции пробел_или_табуляция позиция где имя_функции - это имя, для которого задается точка входа в таблицу переходов, а позиция задает позицию этой точки входа. Позиция может быть целым числом или диапазоном целых чисел в формате позиция1-позиция2. Каждая позиция должна быть не меньше 1; одна и та же позиция не может быть указана дважды; в таблице переходов не должно быть пропущенных позиций (каждое число от 1 до некоторого максимального должно быть обязательно задействовано).

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

#objects Указывает имена об ектных модулей, составляющих разделяемую библиотеку выполнения. Строки, следующие за этой директивой, рассматриваются как список об ектных файлов, в том порядке, в котором они должны быть загружены в разделяемую библиотеку выполнения. Список состоит из имен файлов, разделяемых пробелами или табуляциями. Этот же список используется для определения об ектных файлов для разделяемой библиотеки сборки, но порядок их в разделяемой библиотеке сборки определяется пропуском списка через lorder(1) и tsort(1) . Эта директива может встретиться в файле спецификаций разделяемой библиотеки только однажды.

#objects noload За данной директивой следует список имен разделяемых библиотек сборки. Эти библиотеки просматриваются в том порядке, в котором они указаны, для разрешения ссылок из создаваемой разделяемой библиотеки. При этом ситуация, когда неразделяемая версия об екта обнаруживается раньше разделяемой, трактуется как ошибочная.

Имя разделяемой библиотеки сборки задается как маршрутное имя файла или как аргумент вида -lX, где libX.a является именем файла в каталоге LIBDIR или LLIBDIR. Описываемая ситуация аналогична ситуации с ld(1) , и для задания других каталогов поиска библиотек можно воспользоваться опцией -L.

Отметим, что если с помощью директивы #objects noload указана разделяемая библиотека сборки, то в каждой команде cc, редактирующей связи с создаваемой разделяемой библиотекой, нужно также задавать упомянутую разделяемую библиотеку сборки.

#hide linker [*] Эта директива делает внешние имена локальными для создаваемой разделяемой библиотеки. В последующих строках могут задаваться регулярные выражения [см. sh(1) , find(1) ], и если внешнее имя соответствует хотя бы одному из выражений, оно становится локальным. Оставить некоторые имена внешними можно с помощью директивы #export (см. ниже). Необязательный аргумент * эквивалентен последовательности

#hide linker * и приводит к тому, что все внешние имена становятся локальными.

Все имена, использованные в директивах #init и #branch, всегда являются внешними и не могут быть сделаны локальными с помощью директивы #hide.

#export linker [*] В последующих строках могут задаваться внешние имена, которые из-за соответствия регулярным выражениям директивы #hide могли бы стать локальными. Например, последовательность директив

#hide linker * #export linker one two приводит к тому, что все имена, кроме one и two, а также тех, которые упомянуты в директивах #init и #branch, становятся локальными.

#init об ектный_файл Указывает об ектный файл, который требует инициализации. Строки, следующие за этой директивой, рассматриваются как строки спецификации инициализации, которые должны иметь следующий формат:

Указатель пробел_или_табуляция имя где указатель - это указатель на внешнее имя; указатель должен быть определен в об ектном_файле. Для каждой такой строки генерируются команды инициализации вида: указатель=&имя; Повторная инициализация указателей не допускается, так же, как и повторное использование директивы #init для того же об ектного_файла.

#ident цепочка_символов Задает цепочку_символов, которая будет включена в секцию комментариев (.comment) разделяемой библиотеки выполнения.

## Комментарий. Строки, начинающиеся с ##, игнорируются.

ФАЙЛЫ

LIBDIR Обычно /lib. LLIBDIR Обычно /usr/lib. TMPDIR/* Временные файлы.

Обычно каталог TMPDIR - это /usr/tmp, однако данное соглашение можно изменить, присвоив переменной окружения TMPDIR другое значение [см. tempnam() в tmpnam(3S) ].

СМ. ТАКЖЕ
ar(1) , as(1) , cc(1) , ld(1) , lorder(1) , tsort(1) . a.out(4), ar(4) в Справочнике программиста.

ОГРАНИЧЕНИЯ
Опцию -n нельзя использовать одновременно с директивой #objects noload. Если указать разделяемую библиотеку сборки, которая уже существует, то mkshlib обновит ее с помощью команды ar -ru. Следовательно, если удаляется или переименовывается об ектный файл, включенный в разделяемую библиотеку сборки, то перед обновлением нужно обязательно удалить разделяемую библиотеку сборки.

Статические и динамические библиотеки.

Библиотеками называют «сборники» подпрограмм или объектов, как правило, ориентированных на решение набора близких по тематике задач. С точки зрения их организации и использования библиотеки бывают статическими и динамическими.

Статические библиотеки (static library) могут представлять собой набор исходных кодов, подключаемых программистом в свою программу, либо в виде заранее скомпилированных объектных файлов, связываемых вместе на этапе компиляции. В Windows такие файлы обычно имеют расширение <.lib>. В результате связывания со статической библиотекой, программа включает все используемые ей функции, что увеличивает её размер, но делает более автономной.

Динамические библиотеки (shared library, dynamic link library) загружаются операционной системой по «требованию» запущенной программы уже в ходе её выполнения. Если необходимая библиотека уже была загружена в память, то повторная загрузка не выполняется. При этом один и тот же набор функций или объектов библиотеки может быть использован одновременно несколькими работающими программами, что позволяет эффективно использовать ресурсы оперативной памяти. Динамические библиотеки в Windows обычно имеют расширение <.dll>.

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

Создание динамической библиотеки.

В Microsoft Visual Studio создание проекта для построения динамической библиотеки аналогично привычной процедуре создания проекта косольного приложения. Тип проекта Win32 Console Application, только теперь в мастере создания проекта нужно быдет выбрать пункт «DLL»:

В случае, если такой пункт в мастере отсутствует, можно создать обычный проект Console Application, а затем в свойствах проекта задать его тип:

Configuration Properties => General => Configuration Type: Dynamic Library (.dll)

Declspec(dllexport) void display(const char * str);

#include "dlltest.h"

void display(const char * str)

Модификатор __declspec(dllexport) разрешает экспорт библиотекой указанной функции для использования её другими приложениями.

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

Использование динамической библиотеки.

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

При явном связывании исполняемый файл, использующий библиотеку DLL, должен делать вызовы функции для явной загрузки и выгрузки библиотеки DLL и осуществления доступа к экспортированным функциям библиотеки DLL. Клиентский исполняемый файл вызывает экспортированные функции с помощью указателя функции.

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

В качестве примера воспользуемся механизмом неявного связывания (как наиболее простого) для подключения построенной простой библиотеки.

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

    Подключить соответствующий заголовочный файл “dlltest.h”. Для этого нужно либо использовать путь к файлу непосредственно в директиве #include (если проекты находятся в одной папке решения, то лучше использовать относительный путь), либо в свойствах проекта добавить путь к этому заголовочному файлу в разделе

Configuration Parameters => C/C++ => General => Additional Include Directories

    Использовать файл соответствующей библиотеки импорта. Для этого можно воспользоваться директивой вида (здесь использован относительный путь к построенному lib-файлу).

Кроме того, можно добавить путь к файлу библиотеки импорта в свойствах проекта в разделе

Configuration Parameters => Linker => Input => Additional Dependencies

Теперь создадим основной файл программы с таким содержимым:

#pragma comment(lib, "../Debug/dlltest.lib")

#include "dlltest.h"

display("Hello");

system("pause");

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

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

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

Некоторые старые системы, например, Burroughs MCP , Multics , также имеют только один формат для исполняемых файлов, независимо от того, являются ли они общими. Они имеют файлы общей библиотеки того же формата, что и исполняемые файлы. Это дает два основных преимущества: во-первых, для каждого из них требуется только один загрузчик, а не два (наличие отдельного загрузчика приносит дополнительную сложность). Во-вторых, он также позволяет использовать исполняемые файлы в качестве разделяемых библиотек, если у них есть таблица символов . Типичные форматы комбинированных исполняемых и совместно используемых библиотек: ELF и Mach-O (оба в Unix) и (Windows).

В некоторых более старых средах, таких как 16-битная Windows или MPE для HP 3000 , в е с общей библиотекой допускались только данные на основе стека (локальные), или другие существенные ограничения были наложены на разделяемой библиотеки.

Разделяемая память [ | ]

библиотеки может совместно быть в памяти вместе c процессами , а также на диске. Если используется виртуальная память, процессы будут выполнятся в физической странице ОЗУ, которая отображается в разные адресные пространства процессов. Это имеет свои преимущества. Например, в системе OpenStep приложения часто имеют размер всего несколько сотен килобайт и быстро загружаются; большая часть их а находилась в библиотеках, которые уже были загружены операционной системой для других целей.

Программы могут осуществлять совместное использование ОЗУ с помощью независимого а , как в Unix , что приводит к сложной, но гибкой архитектуре. Это гарантирует, что с помощью различных приемов, таких как предварительное отображение адресного пространства и резервирование страниц для каждой разделяемой библиотеки, имеет большую вероятность совместного использования. Третьим вариантом является одноуровневое хранилище , используемое IBM System/38 и его преемниками.

В некоторых случаях разные версии разделяемых библиотек могут вызывать проблемы, особенно когда библиотеки разных версий имеют одинаковые имена файлов, и используется для разных приложений, установленных в системе, для каждой требуется определённая версия. Такой сценарий известен как DLL hell , названный в честь Windows и OS/2 DLL . Большинство современных операционных систем после 2001 года имеют методы очистки для устранения таких ситуаций или использования «частных» библиотек для конкретных приложений.

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

Использование разделяемых библиотек и/или DLL (в данном случае разница между ними не принципиальна) предполагает ту или иную форму сборки в момент загрузки: исполняемый модуль имеет неразрешенные адресные ссылки и имена библиотек, которые ему нужны. При загрузке эти библиотеки подгружаются и ссылки разрешаются. Проблема здесь в том, что при Подгрузке библиотеки ее нужно переместить, перенастроив абсолютные адресные ссылки в ее коде и данных (см. Главу 3). Если в разных процессах библиотека будет настроена на разные адреса, она уже не будет разделяемой (рис. 5.14)! Если разделяемые библиотеки могут иметь неразрешенные ссылки на другие библиотеки, проблема только усугубляется - к перемещаемым ссылкам добавляются еще и внешние.

Рис. 5.14 . Конфликтующие адреса отображения DLL

В старых системах семейства Unix, использовавших абсолютные загружаемые модули формата a.out , разделяемые библиотеки также поставлялись в формате абсолютных модулей, настроенных на фиксированные адреса. Каждая библиотека была настроена на СВОЙ адрес. Поставщик новых библиотек должен был согласовать этот адрес с разработчиками системы. Это было весьма непрактично, поэтому разделяемых библиотек было очень мало (особенно если не считать те, которые входили в поставку ОС).

Более приемлемое решение этой проблемы реализовано в OS/2 2.x и Win32 (обе эти архитектуры являются развитием систем с единым адресным пространством). Идея состоит в том, чтобы выделить область адресов под загрузку DLL и отображать эту область в адресные пространства всех процессов. Таким образом, все DLL, загруженные в системе, видны всем (рис. 5.15).