Книжная полка Сохранить
Размер шрифта:
А
А
А
|  Шрифт:
Arial
Times
|  Интервал:
Стандартный
Средний
Большой
|  Цвет сайта:
Ц
Ц
Ц
Ц
Ц

Common Intermediate Language и системное программирование в Microsoft .NET

Покупка
Новинка
Артикул: 824933.01.99
Доступ онлайн
1 000 ₽
В корзину
Описаны основы архитектуры платформы .NET и промежуточный язык этой платформы - Common Intermediate Language (CIL). Подробно рассмотрен прием программирования, называемый динамической генерацией кода. Дано введение в многозадачность и описаны подходы к разработке параллельных приложений на базе платформы .NET. Адресовано всем изучающим вопросы создания метаинструментария и разработки компиляторов для различных операционных систем. Курс посвящен системному программированию в .NET. В нем описана архитектура платформы .NET и промежуточный язык этой платформы - Common Intermediate Language (сокращенно CIL). Рассматривается также прием программирования, называемый динамической генерацией кода. В последних лекциях курса обсуждается параллельное программирование, рассматриваются механизмы многозадачности и создание приложений с параллельным выполнением операций, предоставляемых ядром операционной системы Windows. Обсуждается реализация параллельного выполнения кода в .NET, использование библиотечных средств платформы .NET для создания параллельных приложений. Для студентов и преподавателей университетов, а также для специалистов, повышающих свою квалификацию.
Чеповский, А. М. Common Intermediate Language и системное программирование в Microsoft .NET : курс лекций / А. М. Чеповский, А. В. Макаров, С. Ю. Скоробогатов. - Москва : ИНТУИТ, 2016. - 295 с. - ISBN 5-94774-410-4. - Текст : электронный. - URL: https://znanium.ru/catalog/product/2136990 (дата обращения: 06.05.2024). – Режим доступа: по подписке.
Фрагмент текстового слоя документа размещен для индексирующих роботов. Для полноценной работы с документом, пожалуйста, перейдите в ридер.
Common Intermediate Language и системное
программирование в Microsoft .NET

2-е издание, исправленное

Чеповский А.М.
Макаров А.В.
Скоробогатов С.Ю.

Национальный Открытый Университет “ИНТУИТ”
2016

2
УДК 004.72(075.8)
ББК 14
М15
Common Intermediate Language и системное программирование в Microsoft .NET / Макаров А.В.,
Скоробогатов С.Ю., Чеповский А.М. - M.: Национальный Открытый Университет “ИНТУИТ”, 2016
(Основы информатики и математики)
ISBN 5-94774-410-4

Описаны основы архитектуры платформы .NET и промежуточный язык этой платформы - Common
Intermediate Language (CIL). Подробно рассмотрен прием программирования, называемый
динамической генерацией кода. Дано введение в многозадачность и описаны подходы к разработке
параллельных приложений на базе платформы .NET. Адресовано всем изучающим вопросы создания
метаинструментария и разработки компиляторов для различных операционных систем.
Курс посвящен системному программированию в .NET. В нем описана архитектура платформы .NET
и промежуточный язык этой платформы - Common Intermediate Language (сокращенно CIL).
Рассматривается также прием программирования, называемый динамической генерацией кода. В
последних лекциях курса обсуждается параллельное программирование, рассматриваются
механизмы многозадачности и создание приложений с параллельным выполнением операций,
предоставляемых ядром операционной системы Windows. Обсуждается реализация параллельного
выполнения кода в .NET, использование библиотечных средств платформы .NET для создания
параллельных приложений. Для студентов и преподавателей университетов, а также для
специалистов, повышающих свою квалификацию.

(c) ООО “ИНТУИТ.РУ”, 2006-2016
(c) Чеповский А.М., Макаров А.В., Скоробогатов С.Ю., 2006-2016

3
Введение в архитектуру Microsoft .NET Framework

Обзор учебного курса. Предшественники платформы .NET, составные части
платформы .NET, спецификация CLI. Роль типизации в языках программирования.
Ядро и дополнительные элементы системы типов .NET.

Знакомство с .NET

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

Разработка платформы .NET началась в 1998 году. Изначально ей дали рабочее
название Project 42, которое затем было изменено на COM Object Runtime
(сокращенно, COR). Видимо, аббревиатура COR использовалась достаточно
длительное время, так как ее до сих пор можно найти в названиях dll-файлов и именах
библиотечных функций. Потом платформа сменила еще несколько названий: Lightning,
COM+ 2.0, Next Generation Web Services и, в конце концов, стала называться .NET
Framework.

Спецификация основной части платформы .NET стандартизована ассоциацией ECMA
(European Computer Manufacturers Association). Это означает, что корпорация Microsoft
приветствует независимые реализации платформы.

Главные темы

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

Разработка метаинструментов

Создание любого программного продукта подразумевает знакомство программиста с
предметной областью. То есть разработчик бухгалтерской программы должен в какой-
то степени разбираться в бухучете, а создатель Интернет-магазина - в принципах
ведения торговли. Нетрудно догадаться, что создание новых инструментов для
разработки программ требует от программиста знакомства с тем, с чем он и так хорошо
знаком - с разработкой программ! Наверное, поэтому это занятие столь увлекательно.

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

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

Конструирование компиляторов

В структуре практически любого современного компилятора можно выделить, по
крайней мере, две части: “front-end” и “back-end”. “Front-end” осуществляет
лексический и синтаксический анализ программы и переводит программу в некоторое
промежуточное представление. А “back-end” на основе этого промежуточного
представления генерирует код для целевой аппаратной платформы. Между этими
двумя частями может находиться оптимизатор, анализирующий и преобразующий
промежуточное представление программы (см. рис. 1.1).

Рис. 1.1.  Структура современного компилятора

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

Резюмируя вышесказанное, можно сказать, что в большинстве учебников и учебных
курсов, посвященных разработке компиляторов, основное внимание уделяется
алгоритмам лексического и синтаксического анализа, то есть они учат в основном
программированию “front-end’ов”. В нашем учебнике мы концентрируем внимание на
архитектуре и языке целевой платформы (.NET), а также изучаем работу с графами
потоков управления. Это означает, что мы ориентируемся на программирование “back-
end’ов”.

Предшественники платформы .NET

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

UCSD p-System

Операционная система UCSD p-System была разработана в 1978 году в

5
Калифорнийском университете для учебных целей. Главное ее достоинство
заключалось в том, что она могла работать как на компьютерах PDP-11, стоявших в
вычислительном центре университета, так и на домашних микрокомпьютерах
студентов.

Независимость операционной системы от аппаратной платформы достигалась путем
введения понятия виртуальной p-машины (p-Machine), обладавшей собственным
набором инструкций, который назывался p-кодом (p-code). Сама операционная система
и все работавшие в ней программы были закодированы на p-коде, поэтому для того
чтобы запустить их на новой аппаратной платформе, требовалось всего лишь
реализовать для этой платформы интерпретатор p-кода.

Виртуальная p-машина была похожа на обычный компьютер и обладала процессором и
памятью. Программы хранились в памяти машины вместе с данными, а все вычисления
выполнялись через расположенный в памяти стек. Виртуальный процессор содержал
пять регистров, один из которых использовался для хранения адреса текущей
выполняемой инструкции (program counter - PC), а остальные обеспечивали работу со
стеком.

Платформа .NET использует похожую схему обеспечения независимости программ от
аппаратной платформы. Все программы, работающие на платформе .NET,
закодированы на языке CIL (Common Intermediate Language), который представляет
собой набор инструкций некой абстрактной стековой машины. Основное отличие
UCSD p-System от .NET заключается в принципах выполнения программ. Программы,
закодированные в p-коде, непосредственно выполнялись интерпретатором, тогда как
программы на CIL перед выполнением транслируются в код для конкретного
процессора специальным компилятором.

Технология ANDF

Технология ANDF (Architectural Neutral Distribution Format) была разработана в первой
половине 1990-х годов в OSF (Open Software Foundation) для увеличения
переносимости программного обеспечения. Смысл технологии заключается в
разделении процесса компиляции программ на две разнесенные во времени и
пространстве фазы:

1. перевод программы в формат ANDF;
2. трансляция программы, представленной в формате ANDF, в исполняемый файл

при установке программы на компьютер пользователя.

Формат ANDF не зависит ни от языков программирования, ни от особенностей
аппаратных платформ и операционных систем. Программы, распространяемые в
формате ANDF, могут быть установлены на любой платформе, для которой имеется
транслятор из ANDF в исполняемый код.

Схема использования технологии ANDF показана на рис. 1.2. Для каждого языка
программирования реализован компилятор, который генерирует файл в формате

6
ANDF. Такой компилятор называется генератором ANDF (ANDF producer). Для
каждой аппаратной платформы реализован инсталлятор ANDF (ANDF installer),
который переводит программу из формата ANDF в формат исполняемых файлов.

Рис. 1.2.  Схема использования технологии ANDF

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

Платформа Java

Платформа Java по архитектуре и своим возможностям наиболее близка к платформе
.NET. Она была разработана в середине 1990-х годов в Sun Microsystems для бытовых
приборов, подключаемых к компьютерным сетям. Затем произошло стремительное
развитие Internet-технологий, которое способствовало широкому распространению
Java. В настоящее время Java является основным конкурентом платформы .NET.

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

Обзор архитектуры .NET

Платформа .NET состоит из двух основных компонентов. Это Common Language
Runtime и .NET Framework Class Library.

Common Language Runtime (сокращенно CLR) можно назвать “двигателем” платформы
.NET. Его задача - обеспечить выполнение приложений .NET, которые, как правило,
закодированы на языке CIL, рассчитаны на автоматическое управление памятью и
вообще требуют гораздо больше заботы, чем обычные приложения Windows. Поэтому

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

.NET Framework Class Library - это набор классов на все случаи жизни. Далее мы
рассмотрим эту библиотеку подробнее, а сейчас остановимся на двух ключевых
моментах, которые с ней связаны. Во-первых, на платформе .NET реализованы
компиляторы для различных языков программирования, и большинство этих языков
позволяют легко использовать одну и ту же библиотеку классов. То есть .NET
Framework Class Library - это единая библиотека для всех языков платформы .NET. Во-
вторых, использование этой библиотеки позволяет существенно сократить размер
приложений, что способствует их распространению через Internet.

Спецификация CLI

Разработчику системного программного обеспечения важно понимать, что .NET - всего
лишь одна из возможных реализаций так называемой общей инфраструктуры языков
(Common Language Infrastructure, сокращенно CLI), спецификация которой разработана
корпорацией Microsoft.

Можно, руководствуясь этой спецификацией, разработать собственную реализацию
CLI (рис. 1.3). В настоящее время ведутся по крайней мере два посвященных этому
проекта. Это платформа Mono, создаваемая компанией Xamarin, и разрабатываемый в
рамках GNU проект Portable .NET. Кроме того, Microsoft распространяет в исходных
текстах еще одну свою реализацию CLI, работающую как в Windows, так и под
управлением FreeBSD. Эта реализация называется Shared Source CLI (иногда можно
услышать другое название - Rotor).

Рис. 1.3.  Существующие реализации CLI и поддерживаемые ими операционные
системы

Итак, чтобы понять, как работает .NET, необходимо изучить спецификацию CLI. Этим
мы и займемся в ближайшее время, а пока перечислим ее составные части:

Общая система типов (Common Type System, сокращенно CTS) - охватывает
большую часть типов, встречающихся в распространенных языках
программирования.
Виртуальная система исполнения (Virtual Execution System, сокращенно VES) -

8
отвечает за загрузку и выполнение программ, написанных для CLI.
Система метаданных (Metadata System) - предназначена для описания типов,
хранится в независимом от конкретного языка программирования виде,
используется для передачи типовой информации между различными
метаинструментами, а также между этими инструментами и VES.
Общий промежуточный язык (Common Intermediate Language, сокращенно CIL) -
независимый от платформы объектно-ориентированный байт-код, выступающий в
роли целевого языка для любого поддерживающего CLI компилятора.
Общая спецификация языков (Common Language Specification, сокращенно CLS) -
соглашение между разработчиками языков программирования и разработчиками
библиотек классов, в котором определено подмножество CTS и набор правил.
Если разработчики языка реализуют хотя бы определенное в этом соглашении
подмножество CTS и при этом действуют в соответствии с указанными правилами,
то пользователь языка получает возможность использовать любую
соответствующую спецификации CLS библиотеку. То же самое верно и для
разработчиков библиотек: если их библиотеки используют только определяемое в
соглашении подмножество CTS и при этом написаны в соответствии с указанными
правилами, то эти библиотеки можно использовать из любого соответствующего
спецификации CLS языка.

JIT-компиляция

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

Ключевой особенностью выполнения программ в среде .NET является JIT-компиляция.
Аббревиатура JIT расшифровывается как Just-In-Time, и термин JIT-компиляция
можно перевести как компиляция программ “на лету”. JIT-компиляция заключается в
том, что CIL-код, находящийся в запускаемой сборке, тут же компилируется в
машинный код, на который затем передается управление.

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

В .NET реализованы два JIT-компилятора: один компилирует сборку непосредственно
перед ее выполнением, а другой позволяет откомпилировать ее заранее и поместить в
так называемый кэш откомпилированных сборок. JIT-компилятор первого типа
вызывается автоматически при запуске программы, а JIT-компилятор второго типа
реализован в виде служебной программы ngen, которая входит в состав .NET
Framework SDK.

Программу ngen нельзя воспринимать как простой компилятор, позволяющий
превратить сборку .NET в обычное приложение Windows. Дело в том, что
откомпилированная сборка не может быть непосредственно запущена пользователем -

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

Сборка мусора

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

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

1. Преждевременное освобождение памяти (premature free).

Эта беда случается, если мы пытаемся использовать объект, память для которого
была уже освобождена. Указатели на такие объекты называются висящими
(dangling pointers), а обращение по этим указателям дает непредсказуемый
результат.

2. Двойное освобождение (double free).

Иногда бывает важно не перестараться и не освободить ненужный объект дважды.

3. Утечки памяти (memory leaks).

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

4. Фрагментация адресного пространства (external fragmentation).

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

В программах, работающих в среде .NET, все вышеперечисленные ошибки никогда не
возникают, потому что эти программы используют реализованное в CLR
автоматическое управление памятью, а именно - сборщик мусора. Если не вдаваться в
излишние на данном этапе изучения .NET подробности, можно сказать, что работа
сборщика мусора заключается в освобождении памяти, занятой ненужными объектами.
При этом сборщик мусора также умеет “двигать” объекты в памяти, тем самым
устраняя фрагментацию адресного пространства.

10
Доступ онлайн
1 000 ₽
В корзину