управляемый код и неуправляемый код

Что такое управляемый код

Сравните это с запуском программы C/C++, которая также называется «неуправляемым кодом». В мире неуправляемого кода практически за все отвечает программист. Сама программа представляет собой двоичный файл, который операционная система (ОС) загружает в память и запускает. За все остальное — от управления памятью до различных аспектов безопасности — отвечает программист.

Промежуточный язык и выполнение

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

Промежуточный язык иногда называют языком CIL или MSIL.

Взаимодействие неуправляемого кода

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

Аналогично, C# — это язык, позволяющий использовать неуправляемые конструкции, такие как указатели, прямо в коде с помощью так называемого небезопасного контекста, указывающего часть кода, для которой выполнение не управляется средой CLR.

Источник

Взаимодействие управляемого и неуправляемого кода


Автор: Сергей Тепляков
ООО НПП Кронос
Источник: RSDN Magazine #3-2008

Опубликовано: 12.02.2009
Исправлено: 10.12.2016
Версия текста: 1.0

Введение

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

1 Общие принципы

Реализация смешанных приложений (т.е. содержащих управляемый и неуправляемый код) основана на том, что среда CLR поддерживает конструкции, не входящие в общую систему типов CLR, путем указания того, что некоторый тип является непрозрачным (opaque type). Метаданные непрозрачных типов не содержат никакой дополнительной информации о типе, вместо этого хранится только размер экземпляра в памяти.

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

При попытке компиляции этого кода вы получите следующее сообщение об ошибке: “error C3265: cannot declare a managed ‘managed_’ in an unmanaged OpaqueObject”. Проблема в том, что управляемый объект располагается в управляемой куче, и сборщик мусора никак не может узнать о том, что где-то в неуправляемой памяти осталась ссылка на этот объект. Если бы компиляция и выполнение этого кода были возможны, то в произвольный момент времени (после сборки мусора) в неуправляемой памяти остался бы указатель на удаленный объект. А вы ведь знаете, к чему приводит разыменовывание указателя, который указывает на удаленный объект?

Для решения этой проблемы среда CLR предоставляет тип System::Runtime::InteropServices::GCHandle, который поддерживает работу со ссылками на управляемые объекты из неуправляемой памяти. Для создания нового экземпляра GCHandle применяется статическая функция GCHandle::Alloc. В результате выполнения этой функции создается новая ссылка на управляемый объект, и дескриптор ссылки сохраняется в экземпляре типа GCHandle. Тип GCHandle поддерживает полный перечень типов ссылок на управляемый объект, включая слабые (weak references) и зафиксированные (pinned references) ссылки. Любая программа может преобразовать тип GCHandle в IntPtr (который можно безопасно хранить в непрозрачном типе) и обратно. Экземпляр типа GCHandle (и ссылка на управляемый объект) существуют до тех пор, пока не будет явно вызван метод GCHandle.Free.

Приведенный ниже С++-класс демонстрирует использование типа GCHandle для взаимодействия управляемого и неуправляемого кода.

Как видите, получить доступ к управляемому коду из неуправляемого не так и сложно. Но для того, чтобы этот доступ был возможен при компиляции приложения без ключа /clr, придется немного потрудиться.

2 Доступ к управляемому коду из неуправляемого

В предыдущем разделе описаны основные принципы взаимодействия управляемого и неуправляемого кода. Но все примеры компилировались с ключом компилятора /clr, но как быть, если это невозможно? Что делать, если имеется консольное Win32-приложение, служба или приложение, разработанное с использованием MFC, но нет возможности перекомпилировать его с ключом /clr?

управляемый код и неуправляемый код
Рисунок 1. Взаимодействие управляемого и неуправляемого кода.

Для начала рассмотрим набор управляемых классов, написанных на языке C#.

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

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

Этот код компилируется и прекрасно работает, но для его компиляции необходимо использовать ключ /clr, поэтому он не может быть использован в неуправляемом приложением.

Для решения этой проблемы можно воспользоваться тем, что gcroot является тонкой оболочкой над указателем, и единственным членом этого класса является intptr_t. Поэтому экземпляр класса gcroot и intptr_t гарантированно имеют одинаковое представление в памяти.

Другой особенностью управляемых приложений является то, что при их компиляции константа _MANAGED определена и равна 1.

При всем этом класс ManagedObject будет выглядеть следующим образом.

Но проблема еще решена не полностью. Дело в том, что когда клиент из неуправляемого кода создает экземпляр класса ManagedObject, компилятор автоматически генерирует встроенные функции конструктора копирования и оператора присваивания, которые реализуют побитовое копирование объекта. Но побитовое копирование intptr_t приведет к тому, что два объекта gcroot будут содержать один и тот же дескриптор управляемого объекта. В таком случае при удалении второго экземпляра gcroot вы получите неопределенное поведение, что в большинстве случаев выражается в нарушении доступа к памяти.

Решение задачи состоит в том, чтобы объявить экспортируемые функции конструктора копирования и оператора присваивания в заголовочном файле, а реализовать их в соответствующем cpp-файле.

Тогда, если в неуправляемом коде встретится выражение следующего вида:

С помощью класса ManagedWrapper написать оболочку управляемого класса не составит труда. Но лучше создать несколько макросов, ускоряющих этот процесс.

При наличии класса-оболочки управляемого объекта и макроса DECLARE_WRAPPER реализация оболочки над Class1 становится тривиальной.

Ненамного сложнее оболочка для класса Class2.

Использовать классы оболочек достаточно просто.

В результате выполнения этого кода получим следующий результат (рисунок 2).

управляемый код и неуправляемый код
Рисунок 2. Работа с классами-оболочками из неуправляемого кода.

Во время разработки смешанных приложений я столкнулся с проблемой отладки. Для отладки dll-оболочки из неуправляемого приложения необходимо установить значение DebuggerType в Mixed. Значение по умолчанию (Auto) определяет тип загружаемого отладчика по EXE-файлу, поэтому при запуске неуправляемого EXE-файла IDE запускает неуправляемый отладчик, и отлаживать управляемый код нельзя. А когда вы указываете значение Mixed, IDE загружает оба отладчика, и проблемы с отладкой управляемых модулей не возникает.

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

При создании оболочек над классами Class1 и Class2 не была учтена одна важная деталь – обработка исключений. А что будет, если операция в управляемом коде завершится неудачно и будет сгенерировано исключение? Ответ простой – приложение «рухнет».

Рассмотрим класс Class3, который определяет свойство Age, причем значение этого свойства проверяется при установке, и, если оно больше 150 или меньше 0, генерируется исключение System::AgrumentException.

Первое, что нужно сделать – это определить неуправляемый класс исключения, который будет в управляемом коде принимать System::ArgumentException в качестве параметра и предоставлять текстовое описание ошибки для неуправляемого кода.

Все, что нужно сделать в Class3Wrapper – в функции SetAge() перехватить управляемое исключение и сгенерировать соответствующее неуправляемое исключение. Если функция в управляемом коде может генерировать несколько исключений, то нужно создать оболочки для всех этих исключений, или просто перехватить исключение System::Exception. Это уже зависит от потребностей вашего приложения.

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

4 Работа с делегатами

Рассмотрим класс Class4, который реализует событие SampleEvent.

Там, где в управляемом коде используются делегаты, в неуправляемом коде применяются различные варианты обратного вызова. В С++ это могут быть указатели на функцию, указатели на функцию-член, boost::function (а теперь уже и std::tr1::function, для этого нужно скачать с сайта Microsoft Visual C++ 2008 Feature Pack) или любой другой функциональный объект.

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

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

С использованием вспомогательных макросов, Class4Wrapper будет реализован следующим образом.

5 Оболочка над log4net

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

Прежде всего, нужно создать оболочку над интерфейсом log4net::ILog.

Реализация оболочек над log4net::LogManager и log4net::Config::XmlConfigurator еще проще, т.к. эти классы состоят только из статических функций.

Применение оболочки над log4net также достаточно простое.

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

управляемый код и неуправляемый код
Рисунок 3. Работа с log4net из неуправляемого кода.

Конечно, это не вся функциональность библиотеки log4net, которая может вам понадобиться. Но с помощью класса ManagedObject достаточно просто создать оболочки над другими классами.

Заключение

Источник

Что такое управляемый/неуправляемый код в C#?

что такое управляемый и неуправляемый код? Я не понимаю.

12 ответов

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

вот еще одно бесплатное объяснение управляемого кода:

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

этой хорошая статья о предмете.

уроженца код часто является синонимом неуправляемого, но не идентичен.

когда вы думаете о неуправляемые, думайте машин-специфический, машин-ровный код. Как ассемблере x86. Неуправляемый (родной) код компилируется и связывается для запуска непосредственно на процессоре, для которого он был разработан, исключая все ОС на данный момент. Он не переносной, но быстрый. Очень простой, урезанный код.

в нескольких словах, как это возможно:

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

Управляемый Код:
Код, который работает по «договору о сотрудничестве» с общеязыковая среда выполнения. Управляемый код должен предоставлять метаданные необходимые для выполнения оказания услуг, таких как память управления, межъязыковая интеграция, код доступа, и автоматический контроль времени жизни объектов. Весь код на основе Microsoft промежуточный язык (MSIL) выполняется как управляемый код.

ООН-Управляемый Код:
Созданный код без учета соглашения и требования среды выполнения common language. Неуправляемый код выполняется в среде CLR с минимальными службы (например, нет сборки мусора, ограниченная отладка и т. д.).

OTOH,unmanged code что-то специфическое к машине и подготавливает для использования, никакая потребность обрабатывать ее более далее.

Источник

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

Что такое CLR?

CLR (Common language runtime) — общеязыковая исполняющая среда. Она обеспечивает интеграцию языков и позволяет объектам благодаря стандартному набору типов и метаданным), созданным на одном языке, быть «равноправными гражданами» кода, написанного на другом.

Другими словами CLR этот тот самый механизм, который позволяет программе выполняться в нужном нам порядке, вызывая функции, управляя данными. И все это для разных языков (c#, VisualBasic, Fortran). Да, CLR действительно управляет процессом выполнения команд (машинного кода, если хотите) и решает, какой кусок кода (функцию) от куда взять и куда подставить прямо в момент работы программы. Процесс компиляции представлен на рисунке:
управляемый код и неуправляемый код

Компилятор, помимо ассемблера IL создает полные метаданные.

Метаданные — набор из таблиц данных, описывающих то, что определено в модуле. Также есть таблицы, указывающие на что ссылается управляемый модуль (например, импортируемые типы и числа). Они расширяют возможности таких технологий как библиотеки типов и файлы языка описания интерфейсов (IDL). Метаданные всегда связаны с файлом с IL кодом, фактически они встроены в *.exe или *.dll.
Таким образом метаданные это таблицы, в которых есть поля, говорящие о том, что такой-то метод находится в таком-то файле и принадлежит такому-то типу(классу).
Вот как выглядят метаданные для моего примера (таблицы метаданных просто преобразованы в понятный вид с помощью дизассемблера ILdasm.exe. На самом деле это часть *.exe файла программы:

управляемый код и неуправляемый код

Разобравшись с основными понятиями, давайте посмотрим из чего же состоит тот самый управляемый модуль (или просто наш файл ConsoleApplication_Test_Csharp.exe, который выполняет вывод на экран возраста объекта):

Заголовок показывает на каком типе процессора будет выполняться программа. РЕ32 (для 32 и 64 битных ОС) или РЕ32+ (только для 64 битных ОС)
Заголовок CLR — содержит информацию, превращающую этот модуль в управляемый (флаги, версия CLR, точки входа в Main())
Метаданные — 2 вида таблиц метаданных:
1) определенные в исходном коде типы и члены
2) типы и члены, имеющие ссылки в исходном коде.
Код IL — Код, создаваемый компилятором при компиляции кода на C#. Затем IL преобразуется в процессорные команды (0001 0011 1101… ) при помощи CLR (а точнее JIT)

Работа JIT

И так, что же происходит, когда запускается впервые программа?
Сперва происходит анализ заголовка, чтобы узнать какой процесс запустить (32 или 64 разрядный). Затем загружается выбранная версия файла MSCorEE.dll ( C:\Windows\System32\MSCorEE.dll для 32разрядных процессоров)
После чего вызывается метод, расположенный MSCorEE.dll, который и инициализирует CLR, сборки и точку входа функции Main() нашей программы.

Для выполнения какого-либо метода, например System.Console.WriteLine(«Hello „), IL должен быть преобразован в машинные команды (те самые нули и единицы) Этим занимается Jiter или just-in-time compiler.

Сперва, перед выполнением Main() среда CLR находит все объявленные типы (например тип Console).
Затем определяет методы, объединяя их в записи внутри единой “структуры» (по одному методу определенному в типе Console).
Записи содержат адреса, по которым можно найти реализации методов (т.е. те преобразования, которые выполняет метод).

управляемый код и неуправляемый код

При первом обращение к функции WriteLine вызывается JiT-compiler.
JiTer ‘у известны вызываемый метод и тип, которым определен этот метод.
JiTer ищет в метаданных соответствующей сборки — реализацию кода метода (код реализации метода WriteLine(string str) ).
Затем, он проверяет и компилирует IL в машинный код (собственные команды), сохраняя его в динамической памяти.
После JIT Compiler возвращается к внутренней «структуре» данных типа (Console) и заменяет адрес вызываемого метода, на адрес блока памяти с исполняемыми процессорными командами.
После этого метод Main() обращается к методу WriteLine(string str) повторно. Т.к. код уже скомпилирован, обращение производится минуя JiT Compiler. Выполнив метод WriteLine(string str) управление возвращается методу Main().

Из описания следует, что «медленно» работает функция только в момент первого вызова, когда JIT переводит IL код в инструкции процессора. Во всех остальных случаях код уже находится в памяти и подставляется как оптимизированный для данного процессора. Однако если будет запущена еще одна программа в другом процессе, то Jiter будет вызван снова для того же метода. Для приложений выполняемых в х86 среде JIT генерируется 32-разрядные инструкции, в х64 или IA64 средах — соответственно 64-разрядные.

Оптимизация кода. Управляемый и неуправляемый код

IL может быть оптимизирован, т.е. из него будут удалены IL — команды NOP (пустая команда). Для этого при компиляции нужно добавить параметры

Чем же отличается управляемый код от неуправляемого?

Неуправляемый код компилируется для конкретного процессора и при вызове просто исполняется.

В управляемой среде компиляция производится в 2 этапа:

1) компилятор переводит C# код в IL
2) для исполнения нужно перевести IL код в машинный код процессора, что требует доп. динамической памяти и времени (как раз та самая работа JIT).

Взаимодействие с неуправляемым кодом:

— управляемый код может вызывать направляемую функцию из DLL посредствам P/Invoke (например CreateSemaphore из Kernel32.dll).
— управляемый код может использовать существующий COM-компонент (сервер).
— неуправляемый код может использовать управляемый тип (сервер). Можно реализовать COM — компоненты в управляемой среде и тогда не нужно вести подсчет ссылок интерфейсов.

Параметр /clr позволяет скомпилировать Visual С++ код в управляемые IL методы (кроме когда, содержащего команды с ассемблерными вставками ( __asm ), переменное число аргументов или встроенные процедуры ( __enable, _RetrurAddress )). Если этого сделать не получится, то код скомпилируется в стандартные х86 команды. Данные в случае IL кода не являются управляемыми (метаданные не создаются) и не отслеживаются сборщиком мусора (это касается С++ кода).

Система типов

В дополнение хочу рассказать о системе типов CTS, принятой Microsoft.

CTS (Common Type System) — общая система типов в CLR (тип, по-видимому — это аналог класса C#). Это — стандарт, признанный ECMA который описывает определение типов и их поведение. Также определяет правила наследования, виртуальных методов, времени жизни объектов. После регистрации ECMA стандарт получил название CLI ( Common Language Infrastructure)

— CTS поддерживает только единичное наследование (в отличие от С++)
— Все типы наследуются от System.Object (Object — имя типа, корень все остальных типов, System — пространство имен)

По спецификации CTS любой тип содержит 0 или более членов.

Поле — переменная, часть состояния объекта. Идентифицируются по имени и типу.
Метод — функция, выполняющая действие над объектом. Имеет имя, сигнатуру(число параметров, последовательность, типы параметров, возвр. значение функции) и модификаторы.
Свойство — в реализации выглядит как метод (get/set) а для вызывающей стороны как поле ( = ). Свойства позволяют типу, в котором они реализованы, проверить входные параметры и состояние объекта.
Событие — обеспечивает механизм взаимного уведомления объектов.

Public — метод доступен любому коду из любой сборки
Private — методы вызывается только внутри типа
Family (protected) — метод вызывается производными типами независимо от сборки
Assembly (internal) — метод вызывается любым кодом из той же сборки
Family or Assembly
(protected internal) — метод вызывается производными типами из любой сборки и + любыми типами из той же сборки.

CLS (Common Language Specification) — спецификации выпущенная Майкрософт. Она описывает минимальный набор возможностей, которые должны реализовать производители компиляторов, чтобы их продукты работали в CLR. CLR/CTS поддерживает больше возможностей, определенных CLS. Ассемблер IL поддерживает полный набор функций CLR/CTS. Языки (C#, Visual Basic) поддерживает часть возможностей CLR/CTS (в т.ч. минимум от CLS).
Пример на рисунке

управляемый код и неуправляемый код

Пример проверки на соответствие CLS

Атрибут [assembly: CLSCompliant(true)] заставляет компилятор обнаруживать любые доступные извне типы, содержащие конструкции, недопустимые в других языках.

Первое предупреждение: UInt32 Abc() возвращает целочисленное целое без знака. Visaul Basic, например, не работает с такими значениями.
Второе предупрждение: два открытых метода Abc() и abc() — одиноквые и отличаются лишь регистром букв и возвращаемым типом. VisualBasic не может вызывать оба метода.

Убрав public и оставив только sealed class SomeLibraryType оба предупреждения исчезнут. Так как SomeLibraryType по-умолчанию будет internal и не будет виден извне сборки.

Источник

Что такое управляемый или неуправляемый код в программировании?

Я использую определенную команду в моем коде С#, который работает хорошо. Тем не менее, говорят, что он плохо себя ведет в “неуправляемом” коде.

Что такое управляемый или неуправляемый код?

Вот какой текст из MSDN о неуправляемый код.

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

Вот еще несколько дополнительных объяснений об управляемом коде:

Для вашей проблемы:

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

Это хорошая статья на эту тему.

Исполняемая программа, которая запускается сама по себе. Запущенная из операционной системы, программа вызывает и использует программные процедуры в операционной системе, но не требует использования другой программной системы. Программы на языке ассемблера, которые были собраны на машинном языке, и программы на C/C++, скомпилированные на машинном языке для конкретной платформы, являются примерами неуправляемого кода (режим чтения).

Собственный код часто является синонимом Unmanaged, но не идентичен.

Когда вы думаете о неуправляемом, подумайте об машинном коде компьютера. Как язык ассемблера x86. Неуправляемый (собственный) код скомпилирован и связан для запуска непосредственно на процессоре, для которого он был разработан, за исключением всего материала ОС на данный момент. Это не портативный, но быстрый. Очень простой, урезанный код.

Чтобы преобразовать из управляемой переменной, скажем, в неуправляемую, вы должны сами перейти к самому фактическому объекту. Он, вероятно, завернут или упакован в какую-то дополнительную упаковку. Неуправляемые переменные (например, “int”, скажем) – на 32-битной машине – занимают ровно 4 байта. Накладных или дополнительных упаковок нет. Процесс перехода от управляемого к неуправляемому коду – и обратно – называется “ маршалинг“. Это позволяет вашим программам пересекать границу.

В нескольких словах:

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

Управляемый код:
Код, который работает под “договором о сотрудничестве” с общее время выполнения. Управляемый код должен предоставлять метаданные необходимые для среды выполнения для предоставления таких услуг, как память управление, межязыковая интеграция, безопасность доступа к коду и автоматический контроль жизненного цикла объектов. Весь код на основе Microsoft промежуточный язык (MSIL) выполняется как управляемый код.

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

OTOH, невыполненный код – это что-то конкретное для машины и готово к использованию, нет необходимости обрабатывать его дальше.

Управляемый код – это то, что создают компиляторы С#.Net, VB.Net, F #.Net и т.д. Он работает в среде CLR, которая, помимо прочего, предлагает такие службы, как сборка мусора, проверка ссылок и многое другое. Так что подумайте, мой код управляется CLR.

С другой стороны, неуправляемый код компилируется прямо в машинный код. Это не управляет CLR.

Источник

Добавить комментарий

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