Поделиться через


Процесс управляемого выполнения

Процесс управляемого выполнения включает следующие шаги, которые подробно рассматриваются далее в этом разделе:

  1. Выбор компилятора. Чтобы получить преимущества, предоставляемые средой CLR, необходимо использовать один или несколько компиляторов языка, предназначенных для среды выполнения.
  2. Компиляция вашего кода в промежуточный язык. Компиляция преобразует исходный код в общий промежуточный язык (CIL) и создает необходимые метаданные.
  3. Компиляция CIL в машинный код. Во время выполнения компилятор "just-in-time" (JIT) преобразует CIL в машинный код. Во время этой компиляции код должен пройти процесс проверки, который проверяет CIL и метаданные, чтобы узнать, можно ли определить, может ли код быть типобезопасным.
  4. Выполнение кода. Общая среда выполнения (CLR) предоставляет инфраструктуру, которая позволяет выполнять код, а также службы, которые можно использовать в ходе выполнения.

Выбор компилятора

Чтобы получить преимущества, предоставляемые средой CLR, необходимо использовать один или несколько компиляторов языка, предназначенных для среды выполнения, таких как Visual Basic, C#, Visual C++, F#, или один из многих сторонних компиляторов, таких как Eiffel, Perl или COBOL компилятор.

Так как это среда выполнения с несколькими языками, среда выполнения поддерживает широкий спектр типов данных и языковых функций. Используемый компилятор языка определяет доступные функции среды выполнения и разрабатываете код с помощью этих функций. Компилятор, а не среда выполнения, устанавливает синтаксис, который должен использовать код. Если компонент должен быть полностью использоваться компонентами, написанными на других языках, экспортируемые типы компонента должны предоставлять только языковые функции, включенные в спецификацию CLS. Вы можете использовать атрибут CLSCompliantAttribute для обеспечения соответствия вашего кода стандарту CLS. Дополнительные сведения см. в статье о независимости языка и независимых от языка компонентах.

Компиляция в CIL

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

Когда компилятор создает CIL, он также создает метаданные. Метаданные описывают типы в коде, включая определение каждого типа, сигнатуры членов каждого типа, элементы, ссылающиеся на код, и другие данные, используемые средой выполнения во время выполнения. CIL и метаданные содержатся в переносимом исполняемом файле (PE), который основан на опубликованном формате Microsoft PE и расширяет его, а также на формате общего объектного файла (COFF), используемом исторически для содержимого исполняемых файлов. Этот формат файла, который включает CIL или машинный код, а также метаданные, позволяет операционной системе распознавать образы среды CLR. Наличие метаданных в файле вместе с CIL позволяет коду описать себя, что означает, что нет необходимости в библиотеках типов или языке определения интерфейса (IDL). Среда выполнения находит и извлекает метаданные из файла по мере необходимости во время выполнения.

Компиляция CIL в машинный код

Прежде чем запускать общий промежуточный язык (CIL), его необходимо скомпилировать в среде CLR в машинный код для целевой архитектуры компьютера. .NET предоставляет два способа выполнения этого преобразования:

Компиляция компилятором JIT

Компиляция JIT преобразует CIL в машинный код по запросу во время выполнения приложения, когда содержимое сборки загружается и выполняется. Так как среда предоставляет компилятор 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 может привести к нарушению правил безопасности типов. Процесс проверки проверяет четко определенный набор типобезопасного кода и пропускает только код, который является типобезопасным. Однако некоторый типобезопасный код может не пройти проверку из-за некоторых ограничений процесса проверки, а некоторые языки, по проектированию, не создают проверенный типобезопасный код. Если типобезопасный код требуется политикой безопасности, но код не проходит проверку, исключение возникает при запуске кода.

Запуск кода

Общее языковое выполнение предоставляет инфраструктуру, которая позволяет осуществлять управляемое исполнение и сервисы, которые можно использовать в процессе выполнения. Перед запуском метода его необходимо скомпилировать в код, зависящий от процессора. Каждый метод, для которого был создан CIL, компилируется JIT при первом вызове, а затем выполняется. При следующем запуске метода выполняется существующий скомпилированный JIT-код машинного кода. Процесс JIT-компиляции и последующего выполнения кода повторяется до завершения выполнения.

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

В Microsoft Windows Vista загрузчик операционной системы проверяет наличие управляемых модулей, проверяя бит в заголовке COFF. Бит, заданный, обозначает управляемый модуль. Если загрузчик обнаруживает управляемые модули, загружает mscoree.dllи _CorValidateImage и _CorImageUnloading уведомляют загрузчику при загрузке и выгрузке образов управляемых модулей. _CorValidateImage выполняет следующие действия:

  1. Гарантирует, что код является допустимым управляемым кодом.
  2. Изменяет точку входа в образе на точку входа во время выполнения.

В 64-разрядной версии Windows _CorValidateImage изменяет изображение, которое находится в памяти, преобразовав его из PE32 в формат PE32+.

См. также