Операционные системы. Управление ресурсами

       

Компоновка и загрузка



4.2. Компоновка и загрузка

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

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

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

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

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

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

Статическое связывание и загрузка выполняются системными утилитами, не входящими в ядро ОС, они не составляют основной предмет нашего рассмотрения. Алгоритмы функционирования редакторов связей и перемещающих загрузчиков подробно рассматриваются, например, в [3, 9], здесь же мы остановимся на проблемах динамического связывания, выполняемого ОС.

Современные ОС позволяют сочетать статическую компоновку с динамической. Хотя в литературе, посвященной этим ОС, возможность динамического связывания описывается как принципиально новая, на самом деле, она была впервые реализована еще в 1965 году в ОС MULTICS [21], и с тех пор ее механизмы не претерпели значительных изменений. В современных ОС модули, подключаемые к программам динамически, носят название библиотек динамической компоновки (dynamic link library), соответственно, файлы, содержащие образы таких модулей, обычно имеют расширения DLL.

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



Содержание раздела