Файл: Clr компиляция исходного кода в управляемые модули.docx

ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 08.11.2023

Просмотров: 39

Скачиваний: 2

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.


CLR

Компиляция исходного кода в управляемые модули.

CLR (Command Language Runtime) – Обще языковая платформа выполнения языков, которые входят в систему .NET. CLR отвечает за управление памятью, загрузка сборок, безопасность, обработка исключений, синхронизацией. CLR неизвестно на каком языке исполняется код, так как все языки программирования переводятся в промежуточный IL код. Каждый модуль рассматривается как отдельный файл с кодом на IL и метаданными. Сборка состоит из одного и более модулей. CLR это, который позволяет программе выполняться в нужном нам порядке, вызывая функции, управляя данными. И все это для разных языков (c#, VisualBasic, Fortran).

Результат компиляции является программный модуль (managed module) PE32 – 32х разрядный или PE32+ – 64х разрядный. Компиляторы генерируют код, ориентированный на конкретную архитектуру x86, x64.


Часть

Таблица

Заголовок PE32, PE32+

Файл с заголовком PE32 может выполняться в 32 и 64 разрядных версиях Windows, а с заголовками PE32+ только в 64 разрядной. Заголовок обозначает тип файла DLL, GUI.

Заголовок CLR

Хранит версию CLR, точки входа в управляемый модель (Main), месторасположение / размер метаданных модуля.

Метаданные

Каждый управляемый модуль содержит таблицы метаданных. Есть два основных вида таблиц – это таблицы, описывающие типы данных и их члены, определённые в исходном коде и таблицы, описывающие типы данных и их члены, на которые есть ссылки в исходном коде.

IL

Код на IL



Каждый компилятор, который предназначен для CLR помимо генерации IL – кода, должен создавать метаданные. Метаданные – это таблицы данных, описывающие то что определено в модуле (типы и их члены). В метаданных так же есть таблицы, которые указывают на что ссылается модуль (импортируемые члены и их типы). Метаданные всегда встроены в exe или dll файл.

Объединение управляемых модулей

CLR работает со сборками. Сборка обеспечивает группировку одного или несколько модулей или файловых ресурсов (.jpeg, .gif и т.д.).

Компиляторы выполняют работу по преобразованию управляемого модуля в сборку, то есть компилятор C# создаёт управляемый модуль с манифестом.


Загрузка CLR

Можно определять целевую платформу, для которой будет компилироваться IL код. По умолчанию это Any CPU (для x64, x86), но можно выбрать отдельно для x64 или для x86. Можно так же выбрать целевую платформу ARM (архитектура процессора).

Исполнение кода сборки

Непосредственно перед исполнение метода Main() среда CLR находит все типы данных, на которые ссылается программный код метода Main().

  1. В метаданных сборки, реализующий тип Console найти вызываемый метод WriteLine.

  2. Извлечь из метаданных IL – код метода.

  3. Выделить блок памяти

  4. Откомпилировать IL код в команды машинного языка, которые сохраняются в памяти.

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

  6. Передать управление машинному коду, содержащийся в блоке памяти.

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

JIT – компилятор хранит машинные команды в динамической памяти. Это значит, что скомпилированный код уничтожается по завершению работы приложения. При повторном запуске программы JIT придётся заново откомпилировать IL – код в машинные команды, так же при запуске в другом процессе приложения.

  • JIT может оптимизировать машинный код специально для процессора, на котором он выполняется. Например JIT – компилятор может определить, что исполняется на Pentium 4, и сгенерировать машинный код со специальными командам поддерживаемыми Pentium 4. Обычно неуправляемые приложения компилируются с самым общим набором команд и не используют специальные команды, способные повысить эффективность приложения.

  • JIT может определить, что некоторые условия написанные программе на данном компьютере всегда ложные и не будет переводить их в машинные команды. Например, написано условие, если на компьютере кол-во ядер больше какого-то числа, то выполняется некоторый код. JIT может посмотреть, что ядер меньше и не будет компилировать этот код

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

установлена платформа .NET, это необходимо, чтобы IL код исполнялся на CLR

Каждое приложение, ориентированное на .NET, управляется средой CLR ( Common Language Runtime ),

которая является частью .NET Framework и должна быть установлена ​​на компьютере, на котором запущено приложение .NET.

Исключением являются автономные приложения .NET Core . По умолчанию CLR



загружается при каждым запуске приложения .NET (процесс - это запущенная программа) (поэтому CLR запускается как часть приложения .NET).

CLR отвечает за различные вещи во время работы нашего приложения:

выполнение IL ( общий промежуточный язык ),

обеспечение типовой безопасности,

обработка исключений,

управление потоками,

вывоз сборщика мусора.

JIT компилирует IL в полностью машинный код, а не интерпритируется.

Структура памяти

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

которая определяется объемом оперативной памяти, доступной на компьютере.

Однако процесс никогда не оперирует этой «открытой памятью» — прямой доступ к ней имеет только ОС. Когда среда CLR запускается в процессе

.NET, она запрашивает некоторый объем оперативной памяти у операционной системы. Этот кусок памяти называется виртуальным адресным пространством.

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

несколько процессов совместно используют одну и ту же память, но обращаются к разным (отдельным) ее частям.

Разделение памяти

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

создаются следующие структуры данных, представленные в виде кучи :

Code Heap — хранение собственного кода, скомпилированного JIT ,

Малая куча объектов (SOH) — хранение объектов размером менее 85 килобайт,

Куча больших объектов (LOH) — хранение объектов размером более 85 килобайт*,

Оператор MyClass myObj не выделяет место в куче для переменной myObj . Он только создает переменную «OBJREF» в стеке, инициализируя ее значением NULL .

К моменту использования оператора new происходит фактическое выделение памяти в куче и устанавливается значение ссылки.

int i = 8;

string helloText = "Hello";

string result = helloText + i;

происходит запоковка

так как используется метод String.Concat(object, object)

int i = 8;

string helloText = "Hello";

string result = helloText + i.ToString();

нет запоковки используется метод String.Concat(string, string)

Упакованные значения занимают больше места в памяти, чем типы значений, хранящиеся в стеке.


Копирование значения в/из стека также требует затрат. По данным MSDN , упаковка обычно занимает в 20 раз больше времени,

чем простое присваивание ссылки, тогда как распаковка может быть в 4 раза медленнее, чем присваивание.

Несмотря на все последствия упаковки и распаковки для производительности, эти концепции были введены в .NET по следующим причинам:

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

IL (Intermediate Language) — код на специальном языке, напоминающим ассемблер, но написанном для .NET. В него

преобразуется код из других языков верхнего уровня (c#, VisualBasic). Вот тогда-то и пропадает зависимость от

выбранного языка. Ведь все преобразуется в IL (правда тут есть оговорки соответствия общей языковой спецификации CLS, что не входит в рамки данной статьи)

Вот как он выглядит для функции SomeClass::GetAge()

Метаданные — набор из таблиц данных, описывающих то, что определено в модуле. Также есть таблицы, указывающие на

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

библиотеки типов и файлы языка описания интерфейсов (IDL). Метаданные всегда связаны с файлом с IL кодом, фактически они встроены в *.exe или *.dll.

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

Вот как выглядят метаданные для моего примера (таблицы метаданных просто преобразованы в понятный вид с помощью дизассемблера ILdasm.exe. На самом деле это часть

*.exe файла программы:

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

быстро. JIT может генерировать машшинный код оптимальнее для железа (но не всегда). У JIT есть информацио о железе.

FCL (Framework Class Library) - dll которые храняться в .NET сущности которые можем использовать, типы данных и т.д.;

СTS (Common Type System) - спецификация (правило) как должно работать FCL.

CLS - правила, которые должен отвечать язык программирования в .NET для правильного взаимодействия языков программирования в .NET

.NET фреймворк дает разработчикам свободу выбора языка на котором они предпочитают программировать под .NET (C#, VB, C++/CLI …).

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


Это возможно благодаря тому, что .NET фреймворк оперирует только промежуточным языком (IL). IL код создается во время компиляции языковыми компиляторами,

которые транслируют код высокого уровня (c#, vb..) в комбинацию языконезависимого IL кода и его метаданных. IL код вместе с этими метаданными и хедерами

составляет модуль, который называется управляемым модулем.

код во время компиляции будет «переведен» на IL язык с соответствующим определением метаданных и все это станет одним управляемым

модулем, который войдет в управляемую сборку.

Управляемый модуль

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

1. Хедера PE32

2. CLR хедера

3. Метаданных

4. IL кода

Хедера PE32

Информация про исходный код. Например:

для какой архитектуры CPU оптимизирован IL код (PE32 32 bit/64 bit Windows, PE32+ Win 64 bit only)

точка входа (entry point) представляющая адрес в памяти функции

CLR хедер

Инофрмация о CLR (рантайма) версия, точка входа и т.д.

Методанные

Данные о данных

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

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

IL игнорирует пространства имен, тоесть пространства имен из C# кода в IL становятся всего лишь префиксами в «полном» имени типа.

В IL коде каждый член определен в формате полного имени типа как «Namespace.Type:MemberName».

Если дойдет до того, что начнется чистка в поколении 2, объекты, пережившие ее, останутся там же.

Runtime (CLR — единая среда выполнения программ), название которой говорит само за себя - это среда ответственна

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

CLR так же занимается очищение памяти с помощью GC

Корень (корневой объект) — это то, что сохраняет объект в активном состоянии, если на объект нет

прямой или косвенной ссылки со стороны корня, он будет доступен для сборки мусора.

Корнем может выступать:

1. локальная переменная или параметр в выполняющемся методе

2. статическая переменная

3. объект в очереди на финализацию

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

Сборка мусора начинается с того, что сборщик мусора перебирает корневые ссылки и помечает объекты,