Процесс управляемого выполнения
Процесс управляемого выполнения включает следующие шаги, которые подробно рассматриваются далее в этом разделе:
- Выбор компилятора. Чтобы воспользоваться преимуществами среды CLR, необходимо использовать один или несколько языковых компиляторов, обращающихся к среде выполнения.
- Компиляция кода на промежуточный язык. Компиляция преобразует исходный код в общий промежуточный язык (CIL) и создает необходимые метаданные.
- Компиляция CIL в машинный код. Во время выполнения компилятор JIT преобразует CIL в машинный код. Во время этой компиляции код должен пройти процесс проверки, который проверяет CIL и метаданные, чтобы узнать, можно ли определить, может ли код быть типобезопасным.
- Выполнение кода. Среда CLR предоставляет инфраструктуру, обеспечивающую выполнение кода, и ряд служб, которые можно использовать при выполнении.
Выбор компилятора
Чтобы воспользоваться преимуществами, предоставляемыми средой CLR, необходимо применить один или несколько языковых компиляторов, ориентированных на среду выполнения, например компилятор Visual Basic, C#, Visual C++, F# или один из многочисленных компиляторов от независимых разработчиков, например компилятор Eiffel, Perl или COBOL.
Поскольку среда выполнения является многоязычной, она поддерживает широкий набор разнообразных типов данных и языковых средств. Доступные средства среды выполнения определяются используемым языковым компилятором, и разработчики создают код с использованием этих средств. Используемый в коде синтаксис определяется компилятором, а не средой выполнения. Если компонент должен быть полностью использоваться компонентами, написанными на других языках, экспортируемые типы компонента должны предоставлять только языковые функции, включенные в спецификацию CLS. Атрибут CLSCompliantAttribute позволяет гарантировать, что код является CLS-совместимым. Дополнительные сведения см. в разделе Независимость от языка и независимые от языка компоненты.
Компиляция в CIL
При компиляции в управляемый код компилятор преобразует исходный код в общий промежуточный язык (CIL), который является независимым от ЦП набором инструкций, которые можно эффективно преобразовать в машинный код. CIL содержит инструкции по загрузке, хранению, инициализации и вызову методов для объектов, а также инструкции по арифметическим и логическим операциям, потоку управления, прямой доступ к памяти, обработке исключений и другим операциям. Перед запуском кода CIL необходимо преобразовать в код, зависящий от ЦП, обычно с помощью JIT-компилятора. Так как среда CLIT предоставляет один или несколько компиляторов JIT для каждой поддерживаемой архитектуры компьютера, то один и тот же набор CIL можно скомпилировать и запустить в любой поддерживаемой архитектуре.
Когда компилятор создает CIL, он также создает метаданные. Метаданные содержат описание типов в коде, включая определение каждого типа, сигнатуры каждого члена типа, члены, на которые есть ссылки в коде, а также другие сведения, используемые средой выполнения во время выполнения. CIL и метаданные содержатся в переносимом исполняемом файле (PE), который основан на и расширяет опубликованный формат файла MICROSOFT PE и общий файл объекта (COFF), используемый исторически для содержимого исполняемого файла. Этот формат файла, который включает CIL или машинный код, а также метаданные, позволяет операционной системе распознавать образы среды CLR. Наличие метаданных в файле вместе с CIL позволяет коду описать себя, что означает, что нет необходимости в библиотеках типов или языке определения интерфейса (IDL). Среда выполнения находит и извлекает метаданные из файла по мере необходимости при выполнении.
Компиляция CIL в машинный код
Прежде чем запускать общий промежуточный язык (CIL), его необходимо скомпилировать в среде CLR в машинный код для целевой архитектуры компьютера. Платформа .NET предоставляет два способа такого преобразования:
- JIT-компилятор платформы .NET.
- Ngen.exe (генератор образов в машинном коде).
Компиляция компилятором JIT
Компиляция JIT преобразует CIL в машинный код по запросу во время выполнения приложения, когда содержимое сборки загружается и выполняется. Так как среда CLIT предоставляет компилятор JIT для каждой поддерживаемой архитектуры ЦП, разработчики могут создавать набор сборок CIL, которые можно скомпилировать и запускать на разных компьютерах с различными архитектурами компьютеров. Если управляемый код вызывает специфический для платформы машинный API или библиотеку классов, то он будет выполняться только в соответствующей операционной системе.
При JIT-компиляции учитывается возможность того, что определенный код может никогда не вызываться во время выполнения. Вместо использования времени и памяти для преобразования всего CIL в PE-файл в машинный код он преобразует CIL при необходимости во время выполнения и сохраняет полученный машинный код в памяти, чтобы он был доступен для последующих вызовов в контексте этого процесса. Загрузчик создает и присоединяет заглушки к каждому методу в типе, когда тип загружается и инициализируется. При первом вызове метода заглушка передает элемент управления компилятору JIT, который преобразует CIL для этого метода в машинный код и изменяет заглушку, чтобы указать непосредственно в созданный машинный код. Поэтому последующие вызовы метода, скомпилированного с помощью JIT-компилятора, ведут непосредственно к машинному коду.
Создание кода во время установки с помощью NGen.exe
Поскольку компилятор JIT преобразует CIL сборки в машинный код при вызове отдельных методов, определенных в этой сборке, он негативно влияет на производительность во время выполнения. В большинстве случаев снижение производительности приемлемо. Что более важно, код, созданный JIT-компилятором, будет привязан к процессу, вызвавшему компиляцию. Его нельзя сделать общим для нескольких процессов. Чтобы созданный код можно было использовать в нескольких вызовах приложения или в нескольких процессах, которые совместно используют набор сборок, среда CLR предоставляет режим предварительной компиляции. В этом режиме компиляции заранее используется Ngen.exe (генератор собственных образов) для преобразования сборок CIL в машинный код, как и компилятор JIT. Однако, работа Ngen.exe отличается от JIT-компилятора в трех аспектах.
- Он выполняет преобразование из CIL в машинный код перед запуском приложения, а не во время работы приложения.
- При этом сборка компилируется целиком, а не по одному методу за раз.
- Она сохраняет созданный код в кэше образа машинного кода в виде файла на диске.
Проверка кода
В рамках компиляции в машинный код код CIL должен пройти процесс проверки, если администратор не установил политику безопасности, которая позволяет коду обходить проверку подлинности. Проверка проверяет CIL и метаданные, чтобы узнать, является ли код типом безопасным, а это означает, что он обращается только к расположениям памяти, к которым он авторизован для доступа. Типобезопасность помогает изолировать объекты друг от друга и способствует их защите от непредумышленного или злонамеренного повреждения. Она также гарантирует надежное применение условий безопасности для кода.
Среда выполнения основывается на истинности следующих утверждений для поддающегося проверке типобезопасного кода:
- ссылка на тип строго совместима с адресуемым типом;
- для объекта вызываются только правильно определенные операции;
- удостоверения являются подлинными.
В процессе проверки код CIL проверяется в попытке убедиться, что код может получить доступ к расположениям памяти и вызывать методы только через правильные определенные типы. Например, код не должен разрешать доступ к полям объекта так, чтобы можно было выходить за границы расположения в памяти. Кроме того, проверка проверяет код, чтобы определить правильность создания CIL, так как неправильный CIL может привести к нарушению правил безопасности типов. В процессе проверки передается правильно определенный типобезопасный код. Однако иногда типобезопасный код может не пройти проверку из-за ограничений процесса проверки, а некоторые языки по своей структуре не позволяют создавать поддающийся проверке типобезопасный код. Если в соответствии с политикой безопасности использование типобезопасного кода является обязательным и код не проходит проверку, то при выполнении кода создается исключение.
Выполнение кода
Среда CLR предоставляет инфраструктуру, обеспечивающую управляемое выполнение кода, и ряд служб, которые можно использовать при выполнении. Перед выполнением метода его необходимо скомпилировать в код для конкретного процессора. Каждый метод, для которого был создан CIL, компилируется JIT при первом вызове, а затем выполняется. При следующем вызове метода будет выполняться существующий JIT-скомпилированный код. Процесс JIT-компиляции и последующего выполнения кода повторяется до завершения выполнения.
Во время выполнения для управляемого кода доступны такие службы, как сборка мусора, обеспечение безопасности, взаимодействие с неуправляемым кодом, поддержка отладки на нескольких языках, а также поддержка расширенного развертывания и управления версиями.
В Microsoft Windows Vista загрузчик операционной системы выполняет поиск управляемых модулей, анализируя бит в заголовке COFF. Установленный бит обозначает управляемый модуль. При обнаружении управляемых модулей загружается библиотека Mscoree.dll, а подпрограммы _CorValidateImage
и _CorImageUnloading
уведомляют загрузчик о загрузке и выгрузке образов управляемых модулей. Подпрограмма_CorValidateImage
выполняет следующие действия:
- Проверяет, является ли код допустимым управляемым кодом.
- Заменяет точку входа в образе на точку входа в среде выполнения.
В 64-разрядных системах Windows _CorValidateImage
изменяет образ, находящийся в памяти, путем преобразования его из формата PE32 в формат PE32+.