Share via


Arquitectura de integración CLR: entorno hospedado CLR

Se aplica a:SQL ServerAzure SQL Managed Instance

SQL Server integración con Common Language Runtime (CLR) de .NET Framework permite a los programadores de bases de datos usar lenguajes como Visual C#, Visual Basic .NET y Visual C++. Las funciones, procedimientos almacenados, desencadenadores, tipos de datos y agregados pertenecen a los tipos de lógica de negocios que los programadores pueden escribir con estos lenguajes.

CLR incluye memoria recopilada por elementos no utilizados, subprocesos preferentes, servicios de metadatos (reflexión de tipos), comprobación de código y seguridad de acceso al código. CLR usa metadatos para localizar y cargar clases, colocar instancias en memoria, resolver invocaciones a métodos, generar código nativo, exigir mecanismos de seguridad y establecer los límites del contexto en tiempo de ejecución.

ClR y SQL Server difieren como entornos en tiempo de ejecución en la forma en que controlan la memoria, los subprocesos y la sincronización. En este artículo se describe la forma en que se integran estas dos veces de ejecución para que todos los recursos del sistema se administren uniformemente. En este artículo también se describe cómo se integra la seguridad de acceso al código (CAS) clR y la seguridad de SQL Server para proporcionar un entorno de ejecución confiable y seguro para el código de usuario.

Conceptos básicos de la arquitectura CLR

En .NET Framework, un programador escribe un lenguaje de alto nivel que implementa una clase que define su estructura (por ejemplo, los campos o las propiedades de la clase) y sus métodos. Algunos de estos métodos pueden ser funciones estáticas. La compilación del programa genera un archivo denominado ensamblado que contiene el código compilado en el lenguaje intermedio de Microsoft (MSIL) y un manifiesto que contiene todas las referencias a ensamblados dependientes.

Nota

Los ensamblados constituyen un elemento vital para la arquitectura CLR; son las unidades de empaquetado, implementación y control de versiones del código de aplicación de .NET Framework. El uso de ensamblados permite implementar el código de aplicación dentro de la base de datos y proporciona un modo uniforme de administrar, realizar copias de seguridad y restaurar aplicaciones de base de datos completas.

El manifiesto de ensamblado contiene metadatos sobre el ensamblado, que describen todas las estructuras, campos, propiedades, clases, relaciones de herencia, funciones y métodos definidos en el programa. El manifiesto establece la identidad del ensamblado, especifica los archivos que componen la implementación del ensamblado, especifica los tipos y los recursos que forman el ensamblado, desglosa en elementos las dependencias en tiempo de compilación de otros ensamblados y especifica el conjunto de permisos necesarios para que el ensamblado se ejecute correctamente. Esta información se usa en tiempo de compilación para resolver referencias, exigir el cumplimiento de las directivas de enlace de versión y validar la integridad de los ensamblados cargados.

.NET Framework admite atributos personalizados para anotar clases, propiedades, funciones y métodos con información adicional que la aplicación puede capturar en metadatos. Todos los compiladores de .NET Framework usan estas anotaciones sin interpretarlas y las almacenan como metadatos de ensamblado. Estas anotaciones pueden examinarse del mismo modo que cualquier otro metadato.

MSIL ejecuta el código administrado en CLR, en lugar ejecutarlo directamente el sistema operativo. Las aplicaciones de código administrado adquieren servicios de CLR, como la recolección automática de elementos no utilizados, la comprobación de tipos en tiempo de ejecución y la compatibilidad con la seguridad. Estos servicios ayudan a proporcionar un comportamiento uniforme independiente de la plataforma y del lenguaje a las aplicaciones de código administrado.

Diseñar objetivos de integración CLR

Cuando el código de usuario se ejecuta dentro del entorno hospedado en CLR en SQL Server (denominado integración CLR), se aplican los siguientes objetivos de diseño:

Confiabilidad (seguridad)

El código de usuario no debería tener permiso para llevar a cabo operaciones que pongan en peligro la integridad del proceso del motor de base de datos, como mostrar un cuadro de mensaje que solicite una respuesta por parte del usuario o salir del proceso. El código de usuario no debería poder sobrescribir los búferes de memoria del motor de base de datos o las estructuras de datos internas.

Escalabilidad

SQL Server y CLR tienen diferentes modelos internos para la programación y la administración de memoria. SQL Server admite un modelo de subprocesos cooperativo y no preferente en el que los subprocesos producen voluntariamente la ejecución periódicamente, o cuando están esperando bloqueos o E/S. CLR admite un modelo de subprocesos preferente. Si el código de usuario que se ejecuta dentro de SQL Server puede llamar directamente a los primitivos de subproceso del sistema operativo, no se integra bien en el programador de tareas SQL Server y puede degradar la escalabilidad del sistema. CLR no distingue entre la memoria virtual y física, pero SQL Server administra directamente la memoria física y es necesaria para usar la memoria física dentro de un límite configurable.

Los distintos modelos de subprocesamiento, programación y administración de memoria presentan un desafío de integración para un sistema de administración de bases de datos relacionales (RDBMS) que se escala con objeto de admitir miles de sesiones de usuarios simultáneas. La arquitectura debe garantizar que la escalabilidad del sistema no se vea en peligro por el hecho de que el código de usuario llame directamente a las interfaces de programación de aplicaciones (API) para los tipos primitivos de subprocesamiento, memoria y sincronización.

Seguridad

El código de usuario que se ejecuta en la base de datos debe seguir SQL Server reglas de autenticación y autorización al acceder a objetos de base de datos como tablas y columnas. Además, los administradores de bases de datos deben ser capaces de controlar el acceso a los recursos del sistema operativo, como el acceso a archivos y a la red, desde el código de usuario que se ejecuta en la base de datos. Esta práctica es importante, ya que los lenguajes de programación administrados (a diferencia de los lenguajes no administrados como Transact-SQL) proporcionan API para acceder a estos recursos. El sistema debe proporcionar una manera segura de que el código de usuario acceda a los recursos de la máquina fuera del proceso del motor de base de datos. Para más información, consulte CLR Integration Security.

Rendimiento

El código de usuario administrado que se ejecuta en el motor de base de datos debe tener un rendimiento computacional comparable al mismo código que se ejecuta fuera del servidor. El acceso a la base de datos desde el código de usuario administrado no es tan rápido como Transact-SQL nativo. Para obtener más información, consulte Rendimiento de la integración clR.

CLR Services

CLR proporciona una serie de servicios para ayudar a lograr los objetivos de diseño de la integración de CLR con SQL Server.

Comprobación de la seguridad de tipos

El código con seguridad de tipos es código que obtiene acceso a las estructuras de memoria siguiendo métodos perfectamente definidos. Por ejemplo, dada una referencia válida a un objeto, el código con seguridad de tipos puede obtener acceso a la memoria en desplazamientos fijos que se correspondan con miembros de campo reales. Sin embargo, si el código obtiene acceso a la memoria en desplazamientos arbitrarios que se encuentran dentro o fuera del intervalo de memoria perteneciente al objeto, significa que no tiene seguridad de tipos. Cuando los ensamblados se cargan en CLR, antes de que MSIL se compile mediante la compilación Just-In-Time (JIT), el tiempo de ejecución realiza una fase de comprobación que examina el código para determinar su seguridad de tipos. El código que supera correctamente esta comprobación se denomina código con seguridad de tipos comprobable.

Dominios de aplicación

CLR admite la noción de dominios de aplicación como zonas de ejecución dentro de un proceso de host donde los ensamblados de código administrado pueden cargarse y ejecutarse. El límite del dominio de aplicación proporciona aislamiento entre los ensamblados. Los ensamblados se aíslan en lo que se refiere a la visibilidad de variables estáticas y miembros de datos, y a la capacidad de llamar al código de forma dinámica. Los dominios de aplicación también constituyen el mecanismo de carga y descarga de código. Solo es posible descargar código de la memoria descargando el dominio de aplicación. Para obtener más información, consulte Dominios de aplicación y Seguridad de integración clR.

Seguridad de acceso del código (CAS)

El sistema de seguridad de CLR proporciona un modo de controlar qué tipos de operaciones puede llevar a cabo el código administrado mediante la asignación de permisos al código. Los permisos de acceso a código se asignan según la identidad del código (por ejemplo, la firma del ensamblado o el origen del código).

CLR proporciona una directiva de equipos que puede establecer el administrador del equipo. Esta directiva define las concesiones de permisos para cualquier código administrado que se ejecute en el equipo. Además, hay una directiva de seguridad de nivel de host que pueden usar los hosts, como SQL Server, para especificar restricciones adicionales en el código administrado.

Si una API administrada de .NET Framework expone operaciones en recursos protegidos por un permiso de acceso a código, la API solicitará ese permiso antes de obtener acceso al recurso. Esta solicitud hace que el sistema de seguridad de CLR active una comprobación completa de cada unidad de código (ensamblado) en la pila de llamadas. Solo se concederá acceso al recurso si toda la cadena de llamadas tiene permiso.

Tenga en cuenta que la capacidad de generar código administrado dinámicamente, mediante la API Reflection.Emit, no se admite dentro del entorno hospedado en CLR en SQL Server. Dicho código no tendría los permisos CAS necesarios para ejecutarse y, por lo tanto, generaría un error en tiempo de ejecución. Para obtener más información, consulte Seguridad de acceso al código de integración clR.

Atributos de protección del host (HPA)

CLR proporciona un mecanismo para anotar API administradas que forman parte de .NET Framework con determinados atributos que pueden ser de interés para un host de CLR. Algunos ejemplos de estos atributos son los siguientes:

  • SharedState, que indica si la API expone la capacidad de crear o administrar un estado compartido (por ejemplo, campos de clase estática).

  • Synchronization, que indica si la API expone la capacidad de llevar a cabo una sincronización entre los subprocesos.

  • ExternalProcessMgmt, que indica si la API expone una forma de controlar el proceso de host.

Dados estos atributos, el host puede especificar una lista de atributos HPA, como el atributo SharedState, que no deberían permitirse en el entorno hospedado. En este caso, CLR rechaza los intentos del código de usuario de llamar a las API anotadas por los HPA en la lista de atributos prohibidos. Para obtener más información, vea Atributos de protección de host y programación de integración clR.

Cómo trabajan juntos SQL Server y CLR

En esta sección se describe cómo SQL Server integra los modelos de subproceso, programación, sincronización y administración de memoria de SQL Server y CLR. En concreto, en esta sección se examina la integración a la luz de los objetivos de escalabilidad, confiabilidad y seguridad. SQL Server actúa esencialmente como sistema operativo para CLR cuando se hospeda dentro de SQL Server. CLR llama a rutinas de bajo nivel implementadas por SQL Server para la administración de subprocesos, programación, sincronización y memoria. Estas rutinas son las mismas primitivas que usa el resto del motor de SQL Server. Este enfoque proporciona varias ventajas de escalabilidad, confiabilidad y seguridad.

Escalabilidad: subprocesamiento, programación y sincronización comunes

CLR llama a SQL Server API para crear subprocesos, tanto para ejecutar código de usuario como para su propio uso interno. Para sincronizar entre varios subprocesos, CLR llama a SQL Server objetos de sincronización. Esta práctica permite que el programador de SQL Server programe otras tareas cuando un subproceso está esperando en un objeto de sincronización. Por ejemplo, cuando CLR inicia la recolección de elementos no utilizados, todos sus subprocesos esperan a que finalice dicha recopilación de elementos no utilizados. Dado que los subprocesos clR y los objetos de sincronización en los que están esperando se conocen con el programador de SQL Server, SQL Server pueden programar subprocesos que ejecutan otras tareas de base de datos que no implican CLR. Esto también permite SQL Server detectar interbloqueos que implican bloqueos tomados por objetos de sincronización CLR y emplear técnicas tradicionales para la eliminación de interbloqueos.

El código administrado se ejecuta de forma preventiva en SQL Server. El programador de SQL Server tiene la capacidad de detectar y detener subprocesos que no han producido durante un período de tiempo significativo. La capacidad de enlazar subprocesos CLR a SQL Server subprocesos implica que el programador de SQL Server puede identificar subprocesos "descontrolables" en CLR y administrar su prioridad. Dichos subprocesos consecutivos se suspenden y vuelven a colocarse en la cola. Los subprocesos que se identifican repetidamente como subprocesos consecutivos no tienen permiso para ejecutarse durante un período de tiempo determinado de manera que puedan ejecutarse otros subprocesos de trabajo en ejecución.

Hay algunas situaciones en las que el código administrado de larga duración producirá automáticamente y algunas situaciones en las que no lo hará. En las situaciones siguientes, el código administrado de larga duración se producirá automáticamente:

  • Si el código llama al sistema operativo SQL (por ejemplo, para consultar datos)
  • Si se asigna suficiente memoria para desencadenar la recolección de elementos no utilizados
  • Si el código entra en modo preventivo mediante una llamada a funciones del sistema operativo

El código que no realiza ninguna de las anteriores, por ejemplo bucles ajustados que contienen solo cálculos, no producirá automáticamente el programador, lo que puede dar lugar a largas esperas para otras cargas de trabajo del sistema. En estas situaciones, es necesario que el desarrollador produzca explícitamente llamando a la función System.Thread.Sleep() de .NET Framework o especificando explícitamente el modo de preemtivo con System.Thread.BeginThreadAffinity(), en cualquier sección de código que se prevé que se ejecute de forma prolongada. En los ejemplos de código siguientes se muestra cómo producir manualmente con cada uno de estos métodos.

// 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();
Escalabilidad: administración de memoria común

CLR llama a SQL Server primitivos para asignar y desasignar su memoria. Dado que la memoria usada por CLR se tiene en cuenta en el uso total de memoria del sistema, SQL Server puede permanecer dentro de sus límites de memoria configurados y asegurarse de que CLR y SQL Server no compiten entre sí para la memoria. SQL Server también puede rechazar solicitudes de memoria CLR cuando la memoria del sistema está restringida y pedir a CLR que reduzca su uso de memoria cuando otras tareas necesiten memoria.

Confiabilidad: dominios de aplicación y excepciones irrecuperables

Cuando el código administrado de las API de .NET Framework detecta excepciones críticas, como excepciones de memoria insuficiente o desbordamiento de pila, no siempre puede recuperarse de dichos errores y garantizar una semántica coherente y correcta para su implementación. Estas API generan una excepción de anulación de subprocesos en respuesta a estos errores.

Cuando se hospeda en SQL Server, estos subprocesos se controlan de la siguiente manera: CLR detecta cualquier estado compartido en el dominio de aplicación en el que se produce la anulación del subproceso. CLR detecta esto comprobando la presencia de objetos de sincronización. Si hay un estado compartido en el dominio de aplicación, se descarga el propio dominio de aplicación. La descarga del dominio de aplicación detiene las transacciones de base de datos que se estén ejecutando en esos momentos en dicho dominio de aplicación. Dado que la presencia de estado compartido puede ampliar el impacto de estas excepciones críticas a las sesiones de usuario distintas de la que desencadena la excepción, SQL Server y CLR han tomado medidas para reducir la probabilidad de estado compartido. Para obtener más información, vea la documentación de .NET Framework.

Seguridad: conjuntos de permisos

SQL Server permite a los usuarios especificar los requisitos de confiabilidad y seguridad para el código implementado en la base de datos. Cuando los ensamblados se cargan en la base de datos, el autor del ensamblado puede especificar uno de los tres conjuntos de permisos para ese ensamblado: SAFE, EXTERNAL_ACCESS y UNSAFE.

Funcionalidad SAFE EXTERNAL_ACCESS UNSAFE
Seguridad de acceso del código Solo ejecución Ejecución + acceso a recursos externos Sin restricciones
Restricciones del modelo de programación Sin restricciones
Requisito de capacidad de comprobación No
Capacidad de llamar a código nativo No No

SAFE es el modo más confiable y seguro, con restricciones asociadas relativas al modelo de programación permitido. Los ensamblados SAFE tienen permisos suficientes para la ejecución, realización de cálculos y obtención de acceso a la base de datos local. Los ensamblados SAFE deben tener capacidad para comprobar la seguridad de los tipos y no pueden llamar a código no administrado.

UNSAFE está pensado para el código de alta confianza que solo pueden crear los administradores de bases de datos. Este código de confianza no tiene ninguna restricción de seguridad de acceso del código y puede llamar al código no administrado (nativo).

EXTERNAL_ACCESS proporciona una opción de seguridad intermedia, que permite al código tener acceso a los recursos externos a la base de datos, pero manteniendo las garantías de confiabilidad de SAFE.

SQL Server usa el nivel de directiva cas de nivel de host para configurar una directiva de host que conceda uno de los tres conjuntos de permisos en función del conjunto de permisos almacenado en SQL Server catálogos. El código administrado que se ejecuta dentro de la base de datos siempre obtiene uno de estos conjuntos de permisos de acceso a código.

Restricciones del modelo de programación

El modelo de programación para código administrado en SQL Server implica escribir funciones, procedimientos y tipos que normalmente no requieren el uso del estado mantenido en varias invocaciones o el uso compartido de estado en varias sesiones de usuario. Además, como se explicó antes, la presencia de estado compartido puede producir excepciones críticas que tienen un impacto en la escalabilidad y la confiabilidad de la aplicación.

Dadas estas consideraciones, se desaconseja el uso de variables estáticas y miembros de datos estáticos de clases usadas en SQL Server. En el caso de los ensamblados SAFE y EXTERNAL_ACCESS, SQL Server examina los metadatos del ensamblado en tiempo CREATE ASSEMBLY y produce un error en la creación de dichos ensamblados si encuentra el uso de miembros y variables de datos estáticos.

SQL Server tampoco permite llamadas a las API de .NET Framework anotadas con los atributos de protección de host SharedState, Synchronization y ExternalProcessMgmt. Esto impide que los ensamblados SAFE y EXTERNAL_ACCESS llamen a cualquier API que habiliten el estado de uso compartido, realicen la sincronización y afecten a la integridad del proceso de SQL Server. Para obtener más información, consulte Restricciones del modelo de programación de integración de CLR.

Consulte también

Seguridad de la integración CLR
Rendimiento de la integración CLR