Стадии выполнения программы

Материал из Циклопедии
Перейти к навигации Перейти к поиску

В современном программировании от исходного текста до кода в памяти проходит большое число стадий.

Традиционно выделяют стадии редактирования, компиляции, линковки, загрузки, исполнения, и, в процессе разработки — отладки.[1]

На стадии конструирования (design time)[править]

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

Но в некоторых системах программирования (Embarcadero Delphi, Qt Creator) существуют конструкторы форм, когда один компонент программы уже откомпилирован, подключается к конструктору как плагин и вмешивается в разработку остальной программы. Например, в Delphi TEdit (строка ввода) не позволяет произвольно менять высоту — она зависит только от размера шрифта.

Компонент может узнать у среды исполнения, что идёт именно конструирование, а не выполнение, и каким-нибудь образом изменить своё поведение — так, пустой TImage (компонент, показывающий картинку) в Delphi при конструировании пунктирный, а при исполнении — невидимый. К системе могут подключаться и редакторы компонентов — так, TeeChart (даже стартовая версия, поставляющаяся с Delphi) обладает большим окном, которое может визуально изменять любые свойства графика и его рядов.

На стадии компиляции (compile time)[править]

Компилятор проверяет синтаксическую и семантическую правильность исходного текста, и превращает его в предварительные блоки машинного кода.

«На стадии компиляции» — важная концепция в программе. Например, когда заводят статический массив, компилятор должен знать, какого этот массив размера — а значит, размер нужно вычислять при компиляции.

Современные тенденции (например, C++11) расширяют диапазон вещей, которые можно вычислять при компиляции. Например, Паскаль и стандартный Си позволяют вычислять только простейшие выражения. Шаблонами и constexpr-функциями C++ можно вычислить при компиляции довольно сложные конструкции.

На стадии компоновки (link time)[править]

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

Стадия компоновки традиционно важна в языках Си и C++, где программа компилируется как несколько независимых единиц компиляции, и только компоновщик проверяет, каких функций программе недостаёт.

На стадии инсталляции (install time)[править]

Установщик выполняет какие-нибудь действия, специфичные для программы — например, из нескольких версий (без SSE, с SSE и для x64) выбирает нужную и устанавливает именно её. Или собирает начальный файл конфигурации.

На стадии загрузки (load time)[править]

 → Загрузчик программ

Загрузчик собирает образ программы в памяти и динамически скомпоновывает его с динамическими библиотеками, подставляя, как компоновщик, реальные адреса тех или иных процедур.

Какой нужен объём компоновки при загрузке — во многом зависит от архитектуры компьютера. Простая однозадачная ОС, сегментные регистры, виртуальная память и команды ближнего относительного перехода типа jmp [ip+50] снижают количество перестановок. Наличие динамических библиотек — повышает.

Традиционно Unix проводил при загрузке большой объём компоновки. Отголоском этого стал формат исполняемых файлов ELF (Executable and Linkable Format) и компоновщик ld.so (loader, загрузчик).

На стадии исполнения (run time)[править]

Образ программы загружен в память, настало время его исполнять. Логические ошибки и выходы за пределы массива раскрываются именно здесь.

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

  • В большинстве языков проверка типов, генерация кода, распределение переменных по регистрам и оптимизация кода делаются при компиляции или компоновке. Но это может делаться и про выполнении — см. Динамическая типизация, JIT-компиляция.
  • Одна из важных концепций ООП — полиморфизм подтипов, то есть один и тот же код, в зависимости от обрабатываемого типа, вызывает разные функции. Выбор, какую вызывать, происходит, естественно, на стадии исполнения.
  • В Java это сделано для динамической замены классов. Если написано static final int W2 = Screen.WIDTH / 2;, то при загрузке класса система возьмёт Screen.WIDTH и поделит на 2. Когда будет запущен новый процесс с другой шириной экрана, будет вычислен и новый W2.

На стадии инициализации (init time)[править]

Программа ещё не дошла до собственно тела. Однако начал выполняться код, приводящий глобальные переменные в рабочее состояние.

На стадии закрытия (close time)[править]

Аналогично, из тела программы уже вышли, выполняются деструкторы.

Прочие стадии[править]

  • Стадия препроцессирования. Иногда особый препроцессор (например, moc в Qt) делает из исходного текста на языке высокого уровня промежуточный текст на языке более низкого уровня. Располагается перед стадией компиляции или входит в её состав.
  • Стадия обфускации. В некоторых языках программирования (Java) немалое количество оптимизаций кода проводится на этой стадии.

Источники[править]

  1. A short review of high speed compilation / Compiler Compilers and High Speed Compilation: 2nd CCHSC Workshop, Berlin, GDR, October 10-14, 1988. Proceedings