Entorno hospedado CLR
Common Language Runtime (CLR) de Microsoft .NET Framework es un entorno que ejecuta muchos lenguajes de programación modernos, como Microsoft Visual C#, Microsoft Visual Basic y Microsoft Visual C++. CLR presenta memoria de recopilación de elementos no utilizados, subprocesamiento preferente, servicios de metadatos (reflexión de tipos), capacidad de comprobar el código y seguridad de acceso del 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 tema se describe el modo en que estos dos tiempos de ejecución se integran para que todos los recursos del sistema se administren uniformemente. En este tema también se describe la forma en que la seguridad de acceso al código (CAS) y SQL Server de CLR se integran 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. Esto adquiere importancia cuando los lenguajes de programación administrados (a diferencia de los lenguajes no administrados, como Transact-SQL) proporcionan distintas API para obtener acceso a dichos 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 si la cadena de llamadas completa tiene permiso obtendrá acceso al recurso.
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. Estos son los mismos primitivos 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. Esto 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.
Nota
El código administrado de ejecución prolongada que obtiene acceso a datos o asigna suficiente memoria para desencadenar la recolección de elementos no utilizados se producirá automáticamente. El código administrado de ejecución prolongada que no obtiene acceso a datos o no asigna suficiente memoria administrada para desencadenar la recolección de elementos no utilizados debe producirse explícitamente llamando a la función System.Thread.Sleep() de .NET Framework.
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. Para ello, CLR comprueba 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 dicho ensamblado: SAFE, EXTERNAL_ACCESS y UNSAFE.
Conjunto de permisos | 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 | Sí | Sí | Sin restricciones |
Requisito de capacidad de comprobación | Sí | Sí | No |
Capacidad de llamar a código nativo | No | No | Sí |
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 SharedState
atributos de protección de host y ExternalProcessMgmt
. Synchronization
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