Ад Концепций
Что если всё это время понятие жизнь программы недооценивали? Жизнь, а точнее, жизненный цикл программы можно описать повторяющейся последовательностью конечных процессов. Обязательно конечных, в каком-то разумном временном отрезке.
Когда появляется программа? Скорее всего, программа появляется в голове у проектировщика/разработчика, можно назвать это design-time. Но так как этот момент не поддаётся контролю компьютера (пока), то предположим, что моментом появления программы является момент создания минимального запускаемого (о подробном смысле этого термина стоит поговорить отдельно) исходного кода. В контексте Оберона - программа рождается, когда появляется минимальный модуль.
Затем, после этапа написания некоторого программного кода, программа передается компилятору. Компилятор обеспечивает т.н. compile-time - время компиляции. В результате выполнения процесса компиляции мы получаем компилят (то есть непосредственный результат обработки нашего исходного кода).
Во время компиляции наш исходный код влияет на работу компилятора по определенным законам, которые выражены в коде компилятора. В то же самое время, никто не мешает уже на этапе компиляции управлять действиями компилятора (точнее, исполнителя в целом) не опосредованно, через написание текста нашей программы, а непосредственно, через написание кода общего назначения, который будет выполнен компилятором, то есть такого кода, который хоть и относится к задуманной программе, но не переводится компилятором в компилят непосредственно. Так называемый CTFE, но в более общем смысле.
Понятно, что нахождение в контексте процесса работы компилятора может накладывать некоторые ограничения на код времени компиляции, однако может и не накладывать.
Здесь мы можем заметить, что выполнение любого процесса жизненного цикла предполагает наличие результата, в явном или неявном виде.
После получения компилята, над ним, сразу или отложенно должен быть исполнен процесс связывания или линковки. Так как компилят обычно хранится в файле, то возникает время загрузки - load-time. В это время над компилятом могут быть произведены дополнительные преобразования в код целевой платформы, и в это время может быть выполнен код времени загрузки, например, обработка компилята оптимизирующим компилятором. На данный момент известна только одна технология, slim-binaries, поздняя кодогенерация на целевой платформе, которая хоть как-то описывает время загрузки.
Обычно, линковка необходима, так как компилятор всегда производит компилят для одного модуля для непосредственного исполнения на целевой машине. Однако в реальной модульной системе на машине одновременно будут исполнены несколько модулей. Эта ситуация в многомодульной системе приводит к необходимости связывания. В старых системах связывание могло производиться непосредственно после компиляции, но этот вырожденный случай мы не рассматриваем. Итак, возникает время связывания, или link-time.
Мало что известно про выполнение кода во время связывания, однако понятно, что в момент связывания возможно выполнение кода, например, Dependency Injection и динамического наследования, как это может быть реализовано в рамках работы внутри jvm. Такой контроль над еще не запущенным в действие, но уже готовым к исполнению кодом программы позволяет реализовывать автоматизированную настройку и так далее. После связывания и настройки код непосредственно готов к запуску.
Запуск и дальнейшая работа. Представлены временем инициализации (init-time) и временем исполнения (run-time). В сущности, результат работы этого этапа жизненного цикла и является обычно непосредственной целью написания программы. Можно дополнительно выделить время завершения работы программы (close-time). Однако сейчас все три времени работы обычно принято называть run-time, а логическое деление на три этапа реализовывать уже в рамках клиентского программного кода. Такой подход снижает требования к среде исполнения (run-time environment) но не дает гарантий исполнения того или иного этапа в нужной последовательности из-за возможных ошибок исполнения (run-time error) после которых исполнение, обычно, должно завершиться аварийно.
Отдельным важным временем жизни программы является посмертное время (death-time), в которое, вопреки распространенным представлениям тоже является частью жизненного цикла программы. Объяснение тут простое, так как целью работы программы обычно является некий результат, обычно зависящий от входных данных, которых может быть очень много, программы строятся с применением методов, позволяющих итеративно обрабатывать входные данные и производить выходные данные, которые могут быть поданы на вход следующей итерации. Обычно такие данные структурированы (БД), версионны и их содержимое и интерпретация зависит от реализации работающей программы. То есть, программа это алгоритмы и данные, в том числе внешние и степень связанности этих внешних данных с программой никак не влияет на сам факт наличия такой связанности на любом этапе жизненного цикла.
Проще говоря, программа запущенная вновь будет работать по разному, в зависимости от результата, который был ею же получен на прошлом этапе жизненного цикла. И если внести в эти данные изменения во время, которое программа не запущена, программа тоже будет работать иначе, то есть, внося изменения в данные мы программируем программу работать иначе. Что есть этап жизненного цикла программы.
Итак, рассмотрены этапы жизненного цикла программы: compile-time -> load-time -> link-time -> init-time -> run-time -> close-time -> death-time.