Литмир - Электронная Библиотека
A
A

Компиляция

Процедура создания большинства приложений является общей и приведена на рис. 2.2.

Операционная система UNIX - img_14.jpeg

Рис. 2.2. Схема компиляции программы

Первой фазой является стадия компиляции, когда файлы с исходными текстами программы, включая файлы заголовков, обрабатываются компилятором cc(1). Параметры компиляции задаются либо с помощью файла makefile (или Makefile), либо явным указанием необходимых опций компилятора в командной строке. В итоге компилятор создает набор промежуточных объектных файлов. Традиционно имена созданных объектных файлов имеют суффикс ".o".

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

Для достаточно простых задач все фазы автоматически выполняются вызовом команды:

$ make prog

или эквивалентной ей

$ cc -о prog prog.c

которые создают исполняемый файл с именем prog. В этом случае умалчиваемое имя исполняемого файла (a.out) изменено на prog с помощью опции -о.

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

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

$ <b>cc -с file1.c file2.c</b>

$ <b>cc -о prog</b>

Создадим промежуточные объектные файлы file1.o и file2.o

$ <b>cc -o prog file1.o file2.o -lnsl</b>

Создадим исполняемый файл с именем prog, используя промежуточные объектные файлы и библиотеку libnsl.a или libnsl.so

Форматы исполняемых файлов

Виртуальная память процесса состоит из нескольких сегментов или областей памяти. Размер, содержимое и расположение сегментов в памяти определяется как самой программой, например, использованием библиотек, размером кода и данных, так и форматом исполняемого файла этой программы. В большинстве современных операционных систем UNIX используются два стандартных формата исполняемых файлов — COFF (Common Object File Format) и ELF (Executable and Linking Format).

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

□ Какие части программы необходимо загрузить в память?

□ Как создается область для неинициализированных данных?

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

□ Где в памяти располагаются инструкции и данные программы?

□ Какие библиотеки необходимы для выполнения программы?

□ Как связаны исполняемый файл на диске, образ программы в памяти и дисковая область свопинга?

На рис. 2.3 приведена базовая структура памяти для процессов, загруженных из исполняемых файлов форматов COFF и ELF, соответственно. Хотя расположение сегментов различается для этих двух форматов, основные компоненты одни и те же. Оба процесса имеют сегменты кода (text), данных (data), стека (stack). Как видно из рисунка, размер сегментов данных и стека может изменяться, а направление этого изменения определяется форматом исполняемого файла. Размер стека автоматически изменяется операционной системой, в то время как управление размером сегмента данных производится самим приложением. Эти вопросы мы подробно обсудим в разделе "Выделение памяти" далее в этой главе.

Операционная система UNIX - img_15.jpeg

Рис. 2.3. Исполняемые образы программ форматов COFF и ELF

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

Формат ELF

Формат ELF имеет файлы нескольких типов, которые до сих пор мы называли по-разному, например, исполняемый файл или объектный файл. Тем не менее стандарт ELF различает следующие типы:

1. Перемещаемый файл (relocatable file), хранящий инструкции и данные, которые могут быть связаны с другими объектными файлами. Результатом такого связывания может быть исполняемый файл или разделяемый объектный файл.

2. Разделяемый объектный файл (shared object file) также содержит инструкции и данные, но может быть использован двумя способами. В первом случае, он может быть связан с другими перемещаемыми файлами и разделяемыми объектными файлами, в результате будет создан новый объектный файл. Во втором случае, при запуске программы на выполнение операционная система может динамически связать его с исполняемым файлом программы, в результате чего будет создан исполняемый образ программы. В последнем случае речь идет о разделяемых библиотеках.

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

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

Операционная система UNIX - img_16.jpeg

Рис. 2.4. Структура исполняемого файла в формате ELF

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

Поскольку заголовок ELF-файла определяет его структуру, рассмотрим его более подробно (табл. 2.4).

Таблица 2.3. Поля заголовка ELF-файла

Поле Описание
е_ident[]
Массив байт, каждый из которых определяет некоторую общую характеристику файла: формат файла (ELF), номер версии, архитектуру системы (32-разрядная или 64-разрядная) и т.д.
e_type
Тип файла, поскольку формат ELF поддерживает несколько типов
e_machine
Архитектура аппаратной платформы, для которой создан данный файл. В табл. 2.4 приведены возможные значения этого поля
e_version
Номер версии ELF-формата. Обычно определяется как EV_CURRENC (текущая), что означает последнюю версию
e_entry
Виртуальный адрес, по которому системой будет передано управление после загрузки программы (точка входа)
e_phoff
Расположение (смещение от начала файла) таблицы заголовков программы
е_shoff
Расположение таблицы заголовков секций
е_ehsize
Размер заголовка
e_phentsize
Размер каждого заголовка программы
e_phnum
Число заголовков программы
e_shentsize
Размер каждого заголовка сегмента (секции)
е_shnum
Число заголовков сегментов (секций)
e_shstrndx
Расположение сегмента, содержащего таблицу строк
32
{"b":"272553","o":1}