Архитектура интеграции со средой CLR — среда размещения CLR

Применимо к: SQL Server Управляемый экземпляр SQL Azure

SQL Server интеграция со средой CLR платформа .NET Framework позволяет программистам баз данных использовать такие языки, как Visual C#, Visual Basic .NET и Visual C++. С помощью этих языков программисты могут создавать различные объекты бизнес-логики, например: функции, хранимые процедуры, триггеры, типы данных и агрегаты.

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

Среда CLR и SQL Server отличаются от сред среды выполнения тем, как они обрабатывают память, потоки и синхронизацию. В этой статье описывается, как эти два времени выполнения интегрированы, чтобы все системные ресурсы управляли единообразно. В этой статье также рассматривается способ интеграции безопасности доступа к коду СРЕДЫ CLR (CAS) и безопасности SQL Server для обеспечения надежной и безопасной среды выполнения пользовательского кода.

Основные понятия архитектуры CLR

На платформе .NET Framework программист использует язык высокого уровня, который реализует класс, определяющий его структуру (например, поля или свойства класса) и методы. Некоторые из этих методов могут быть статическими функциями. Компиляция программы создает файл, называемый сборкой, которая содержит скомпилированный код на промежуточном языке Майкрософт (MSIL), и манифест, содержащий все ссылки на зависимые сборки.

Примечание

Сборки — важнейший элемент архитектуры CLR. Они представляют собой средства упаковки, развертывания и управления версиями кода приложений на платформе .NET Framework. С помощью сборок можно развертывать код приложений в базе данных и предоставлять единообразный способ администрирования, создания резервных копий и восстановления законченных приложений базы данных.

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

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

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

Цели проектирования при интеграции со средой CLR

При выполнении пользовательского кода в среде, размещенной в СРЕДЕ CLR, в SQL Server (называемой интеграцией со средой CLR), применяются следующие цели проектирования:

Надежность (безопасность)

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

Масштабируемость

SQL Server и среда CLR имеют разные внутренние модели планирования и управления памятью. SQL Server поддерживает совместную модель потоков без вытеснения, в которой потоки периодически выдают выполнение или ожидают блокировки или операции ввода-вывода. Среда CLR поддерживает модель потоков с вытеснением. Если пользовательский код, выполняющийся внутри SQL Server, может напрямую вызывать примитивы потоков операционной системы, то он не интегрируется в планировщик задач SQL Server и может снизить масштабируемость системы. Среда CLR не различает виртуальную и физическую память, но SQL Server напрямую управляет физической памятью и требуется использовать физическую память в пределах настраиваемого ограничения.

Если должно быть обеспечено масштабирование системы управления реляционной базой данных (СУРБД) в целях одновременной поддержки тысяч пользовательских сеансов, то приходится решать сложные задачи интеграции в связи с применением разных моделей организации потоков, планирования и управления памятью. Создаваемая архитектура должна гарантировать, чтобы на масштабируемость системы не оказывал отрицательное влияние непосредственный вызов в пользовательском коде прикладных программных интерфейсов (API) в целях применения базовых функций управления потоками, памятью и синхронизацией.

Безопасность

Пользовательский код, выполняющийся в базе данных, должен соответствовать SQL Server правилам проверки подлинности и авторизации при доступе к объектам базы данных, таким как таблицы и столбцы. Кроме того, администраторы баз данных должны иметь возможность управлять доступом к ресурсам операционной системы, таким как файлы и сетевой доступ, осуществляемым из пользовательского кода, который выполняется в базе данных. Эта практика становится важной, так как управляемые языки программирования (в отличие от неуправляемых языков, таких как Transact-SQL) предоставляют API для доступа к таким ресурсам. Система должна обеспечить безопасный способ доступа пользовательского кода к ресурсам компьютера за пределами процесса ядра СУБД. Дополнительные сведения см. в статье CLR Integration Security.

Производительность

Управляемый пользовательский код, выполняемый в компоненте Database Engine, должен иметь вычислительную производительность, сравнимую с тем же кодом, выполняемым за пределами сервера. Доступ к базе данных из управляемого пользовательского кода выполняется не так быстро, как собственный Transact-SQL. Дополнительные сведения см. в разделе Производительность интеграции со средой CLR.

Службы CLR

Среда CLR предоставляет ряд служб для достижения целей проектирования интеграции среды CLR с SQL Server.

Проверка безопасности типов

Типизированный код — это код, который обращается к структурам памяти только строго определенными способами. Например, при наличии допустимой ссылки на объект типизированный код может обращаться к памяти с фиксированными смещениями, соответствующими фактическим полям элементов. Но если код обращается к памяти с произвольными смещениями внутри и вне области памяти, которая принадлежит к объекту, то код нетипизированный. Если выполняется загрузка сборок в среду CLR до компиляции MSIL с использованием JIT-компиляции, то на этапе проверки во время выполнения код анализируется, чтобы можно было определить безопасность типов. Код, успешно прошедший эту проверку, называется «проверяемым типизированным кодом».

Домены приложений

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

CAS (Code Access Security — безопасность доступа кода)

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

Среда CLR обеспечивает применение политики уровня компьютера, которую может назначить администратор компьютера. Эта политика предоставляет разрешения для любого управляемого кода, выполняемого на компьютере. Кроме того, существует политика безопасности на уровне узла, которую могут использовать узлы, такие как SQL Server для указания дополнительных ограничений на управляемый код.

Если управляемый API на платформе .NET Framework предоставляет операции над ресурсами, защищенные набором разрешений на доступ к коду, то API потребует разрешение перед доступом к ресурсу. В силу этого требования система безопасности CLR запускает исчерпывающую проверку каждого блока кода (сборки) в стеке вызова. Доступ к ресурсу будет предоставлен только в том случае, если вся цепочка вызовов имеет разрешение.

Обратите внимание, что возможность динамического создания управляемого кода с помощью API Reflection.Emit не поддерживается в среде, размещенной в среде CLR в SQL Server. Такой код не будет иметь разрешений CAS для выполнения и поэтому завершится неудачей во время выполнения. Дополнительные сведения см. в разделе ClR Integration Code Access Security.

Атрибуты защиты узла (HPA)

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

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

  • Synchronization, который указывает, обеспечивает ли API возможность выполнять синхронизацию между потоками.

  • ExternalProcessMgmt, который указывает, предоставляет ли API возможность управлять основным процессом.

С учетом этих атрибутов узел может указать список атрибутов защиты узла, таких как атрибут SharedState, которые должны быть запрещены в управляемой среде. В этом случае среда CLR запрещает попытки пользовательского кода, которые направлены на вызов API, внесенных атрибутами защиты узла в запрещенный список. Дополнительные сведения см. в разделах Атрибуты защиты узла и Программирование интеграции со средой CLR.

Совместная работа SQL Server и среды CLR

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

Масштабируемость: общие потоки, планирование и синхронизация

СРЕДА CLR вызывает SQL Server API для создания потоков, как для выполнения пользовательского кода, так и для собственного внутреннего использования. Для синхронизации между несколькими потоками среда CLR вызывает SQL Server объекты синхронизации. Это позволяет планировщику SQL Server планировать другие задачи, когда поток ожидает объекта синхронизации. Например, когда среда CLR запускает сбор мусора, все потоки ожидают завершения сбора мусора. Так как SQL Server планировщику известны потоки СРЕДЫ CLR и объекты синхронизации, которые они ожидают, SQL Server могут планировать потоки, выполняющие другие задачи базы данных, не связанные со средой CLR. Это также позволяет SQL Server обнаруживать взаимоблокировки, связанные с блокировками объектов синхронизации CLR, и использовать традиционные методы удаления взаимоблокировок.

Управляемый код выполняется упреждающ в SQL Server. Планировщик SQL Server может обнаруживать и останавливать потоки, которые не возвращались в течение значительного времени. Возможность подключения потоков СРЕДЫ CLR для SQL Server потоков подразумевает, что планировщик SQL Server может определять "беглые" потоки в среде CLR и управлять их приоритетом. Такие вышедшие из-под контроля потоки приостанавливаются и возвращаются в очередь. Потокам, которые были повторно обнаружены как вышедшие из-под контроля, запрещается выполняться в течение определенного периода времени, чтобы обеспечить выполнение других рабочих потоков.

В некоторых ситуациях длительный управляемый код возвращается автоматически, а в некоторых — нет. В следующих ситуациях длительный управляемый код возвращается автоматически:

  • Если код вызывает ОС SQL (например, для запроса данных)
  • Если выделено достаточно памяти для активации сборки мусора
  • Если код переходит в режим вытеснения путем вызова функций ОС

Код, который не выполняет ни одного из описанных выше действий, например жесткие циклы, содержащие только вычисления, не будет автоматически возвращать планировщик, что может привести к длительному ожиданию других рабочих нагрузок в системе. В таких ситуациях разработчик должен явным образом предоставить результат, вызвав функцию System.Thread.Sleep() платформа .NET Framework или явно введя режим вытеснения с помощью System.Thread.BeginThreadAffinity() во всех разделах кода, которые, как ожидается, будут работать долго. В следующих примерах кода показано, как вручную получить доход с помощью каждого из этих методов.

// Example 1: Manually yield to SOS scheduler.
for (int i = 0; i < Int32.MaxValue; i++)
{
 // *Code that does compute-heavy operation, and does not call into
 // any OS functions.*

 // Manually yield to the scheduler regularly after every few cycles.
 if (i % 1000 == 0)
 {
   Thread.Sleep(0);
 }
}
// Example 2: Use ThreadAffinity to run preemptively.
// Within BeginThreadAffinity/EndThreadAffinity the CLR code runs in preemptive mode.
Thread.BeginThreadAffinity();
for (int i = 0; i < Int32.MaxValue; i++)
{
  // *Code that does compute-heavy operation, and does not call into
  // any OS functions.*
}
Thread.EndThreadAffinity();
Масштабируемость: общее управление памятью

Вызовы СРЕДЫ CLR SQL Server примитивы для выделения и деоквации памяти. Так как память, используемая средой CLR, учитывается в общем объеме использования памяти системой, SQL Server может оставаться в пределах настроенных пределов памяти и гарантировать, что среда CLR и SQL Server не конкурируют друг с другом за память. SQL Server также может отклонить запросы памяти CLR, если системная память ограничена, и попросить CLR сократить использование памяти, когда другим задачам требуется память.

Надежность: домены приложений и неустранимые исключения

Когда управляемый код в API .NET Framework встречает критические исключения, такие как нехватка памяти или переполнение стека, не всегда удается выполнить восстановление после отказа и обеспечить согласованную и правильную семантику для их реализации. Эти API активизируют исключение прерывания потока в ответ на такие отказы.

При размещении в SQL Server такие прерывания потока обрабатываются следующим образом: среда CLR обнаруживает общее состояние в домене приложения, в котором происходит прерывание потока. Среда CLR обнаруживает это путем проверки наличия объектов синхронизации. Если в домене приложения имеется общее состояние, то выгружается сам домен приложения. В результате выгрузки домена приложения останавливаются транзакции базы данных, выполняемые в это время в домене приложения. Поскольку наличие общего состояния может расширить влияние таких критических исключений на сеансы пользователей, отличные от того, который вызывает исключение, SQL Server и среда CLR приняли меры для снижения вероятности общего состояния. Дополнительные сведения см. в документации по .NET Framework.

Безопасность: наборы разрешений

SQL Server позволяет пользователям указывать требования к надежности и безопасности для кода, развернутого в базе данных. При отправке сборок в базу данных автор сборки может указать один из трех наборов разрешений для этой сборки: SAFE, EXTERNAL_ACCESS и UNSAFE.

Функциональность SAFE EXTERNAL_ACCESS UNSAFE
Управление доступом для кода Только выполнение Выполнение и доступ к внешним ресурсам С неограниченным доступом
Ограничения модели программирования Да Да Нет ограничений
Требование к проверяемости Да Да Нет
Возможность вызова машинного кода Нет Нет Да

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

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

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

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

Ограничения модели программирования

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

Учитывая эти факторы, мы не рекомендуем использовать статические переменные и статические элементы данных классов, используемых в SQL Server. Для сборок SAFE и EXTERNAL_ACCESS SQL Server проверяет метаданные сборки во время CREATE ASSEMBLY и не создает такие сборки, если обнаруживает использование статических элементов данных и переменных.

SQL Server также запрещает вызовы API платформа .NET Framework, которые помечены атрибутами защиты узла SharedState, Synchronization и ExternalProcessMgmt. Это не позволяет сборкам SAFE и EXTERNAL_ACCESS вызывать любые API, которые обеспечивают совместное использование состояния, выполняют синхронизацию и влияют на целостность процесса SQL Server. Дополнительные сведения см. в разделе Ограничения модели программирования интеграции CLR.

См. также:

Безопасность интеграции со средой CLR
Производительность интеграции со средой CLR