3.- Funcionamiento Interno del CLR


3.1.- Especificación CLI

Infraestructura de Lenguaje Común (CLI)

·         Especificación patrocinada por Microsoft, Intel, HP y estandarizada por ECMA (2001) e ISO (2003) que describe:

-          Entorno de Ejecución de Aplicaciones

-          Conjunto de Librerías Básicas (BCL)

-          Tipos de Datos Comunes (CTS)

·         El .NET Framework y el .NET Compact Framework son implementaciones de la especificación CLI

La infraestructura de lenguaje común (Common Language Infrastructure, CLI) es una especificación estandarizada que describe un entorno virtual para la ejecución de aplicaciones, cuya principal característica es la de permitir que aplicaciones escritas en distintos lenguajes de alto nivel puedan luego ejecutarse en múltiples plataformas tanto de hardware como de software sin necesidad de reescribir o recompilar su código fuente.

Si bien el CLI tuvo sus orígenes en Microsoft (en principio se pensaba desarrollar un entorno de ejecución compartido para COM con el nombre de Common Object Runtime, que luego de extendió y generalizó para dar lugar a CLI), sus especificaciones fueron llevadas ante ECMA (European Computer Manufacturers Association), una importante organización europea de estándares, para su estandarización en el año 2000. Luego de un año de trabajo conjunto entre ECMA, Microsoft y otras empresas que co-sponsorearon el proceso (Intel, HP, IBM y Fujitsu entre otras), el estándar ECMA-335 que define el entorno CLI finalmente vio la luz en diciembre de 2001. En abril del año 2003 ISO ratificó este estándar con el denominación ISO/IEC 23271:2003 .

Para comprender mejor la inclusión de cada una de las partes principales de la arquitectura de CLI es interesante analizar los objetivos de diseño que se plantearon desde su concepción. Según su especificación, la arquitectura de CLI debe:

          Permitir escribir componentes ínter operables independientemente de la plataforma subyacente y del lenguaje de programación utilizado.

          Exponer todas las entidades programáticas a través de un único sistema unificado de tipos (en la especificación, este sistema es conocido como CTS, o Common Type System).

          Empaquetar todos los tipos en unidades completamente auto descriptivas y portables.

          Cargar los tipos de forma tal que se encuentren aislados unos de otros en tiempo de ejecución, pero que puedan a su vez compartir recursos.

          Resolver dependencias entre tipos en tiempo de ejecución usando una política flexible que pueda tener en cuenta la versión, atributos de localización y políticas administrativas.

          Ejecutar aplicaciones bajo la supervisión de un entorno privilegiado que permita controlar y hacer cumplir políticas en tiempo de ejecución.

          Diseñar toda la infraestructura y servicios basándose en metadatos extensibles, de manera tal que toda la arquitectura pueda acomodarse con poco impacto a nuevas incorporaciones y cambios.

          Poder realizar tareas de bajo nivel, como carga de tipos en memoria, linkeo con librerías y compilación a código nativo sólo cuando sea necesario (este enfoque se conoce típicamente como “on demand”, o “just in time”).

          Proveer una serie de funcionalidades comunes mediante un grupo de librerías de programación que los desarrolladores puedan utilizar para construir sus aplicaciones.

Microsoft .NET de hecho es un súper conjunto de esta especificación, es decir, provee todo lo necesario para cumplir con la misma y además agrega una serie de herramientas, librerías y funcionalidades no contempladas por ella originalmente y que proveen una enorme utilidad y flexibilidad a los desarrolladores (por ejemplo, librerías para la creación de aplicaciones y servicios web, acceso a motores de bases de datos, controles gráficos, herramientas para desensamblar assemblies, debuggers, etc.). Si bien es gratuito, su código fuente no es abierto, y es distribuido por Microsoft en versiones para sistemas operativos Windows 98 y sus sucesores únicamente.

Sub-Especificaciones de CLI

3.2.- Modelo de Ejecución

 

Modelo de Ejecución del CLR

La imágen representa el modelo de compilación y ejecución de aplicaciones .NET, al cual muchas veces se denomina “de compilación diferida”, o “de compilación en dos etapas”. Esto es asi ya que el primer paso para poder ejecutar una aplicación dentro del CLR es compilar su código fuente para obtener un assembly con código MSIL. Este paso es realizado por cada uno de los compiladores de los distintos lenguajes de alto nivel soportados por .NET.

Luego, el CLR se encarga de compilar el código MSIL a código nativo que hace uso específico de los servicios del sistema operativo y la plataforma de hardware subyacente.

Todos los compiladores de los nuevos lenguajes .NET de Microsoft siguen este modelo de ejecución, con excepción de C++ .NET, que es el único lenguaje al que se le ha dejado la capacidad de emitir también código “no manejado”. Esto se debe a que ciertas aplicaciones, como los drivers de dispositivos, necesitan tener acceso a los recursos del sistema operativo a muy bajo nivel para lograr un rendimiento óptimo y mayor performance.

Modelo de Ejecución del CLR

Aqui podemos ver el modelo de ejecución del CLR en acción. Sus tres pasos principales son:

1)       Desarrollo, cuya salida es una aplicación .NET compuesta de uno o más assemblies

2)       Instalación o Deployment:  es el proceso de instalar y configurar la aplicación .NET en el dispositivo físico en el que debe ejecutarse

3)       Ejecución: aqui es donde los componentes del CLR empiezan a actuar, cargando el assembly en memoria, revisando su metadata y política de seguridad para ver si puede ejecutarse o no, cargando las clases y compilándolas luego a código nativo a medida que se las va utilizando.

3.3.- Application Domains

  •          Procesos virtuales dentro del CLR

-          Se ejecutan dentro de un proceso del Sistema Operativo

-          Un proceso del sistema operativo puede contener varios AppDomains

-          Más eficiente que múltiples procesos del sistema operativo

-          Más eficiente en el intercambio de contexto de ejecución

  •  Un Assembly y sus tipos son siempre cargados dentro de un AppDomain

  • Provee una frontera para: Fallos, Tipos, Seguridad

 

Los sistemas operativos y los entornos de ejecución normalmente proveen alguna forma de aislamiento entre aplicaciones, lo cual es necesario para asegurar que una aplicación no pueda afectar de manera negativa a otra que se está ejecutando simultáneamente.

Un Application Domain (o Frontera de Aplicación) es la mínima unidad de aislamiento de aplicaciones dentro del CLR, y todos los assemblies que conforman una aplicación siempre son cargados dentro de uno. El aislamiento entre aplicaciones garantiza que:

          Una aplicación pueda ser detenida independientemente del resto

          Una aplicación no pueda acceder diractamente a código en ejecución o recursos de otra aplicación (el CLR prohibe invocaciones directas entre objetos cargados en distintos Application Domains)

          Una falla en una aplicación no afecte al resto de las aplicaciones en ejecución.

Applications Domains - CLR Host

El sistema operativo Windows no provee soporte para ejecutar directamente una aplicación .NET mediante el CLR, sino que este soporte es provisto por los llamados “CLR Hosts”.

Un CLR Host en una aplicación responsable por cargar el CLR en un proceso del sistema operativo, crear los application domains necesarios dentro de ese proceso y ejecutar la aplicación dentro de los application domains.

3.4.- Common Type System

CTS (Common Type System)

  • Define un conjunto común de “tipos” de datos orientados a objetos
  • Todo lenguaje de programación .NET debe implementar los tipos definidos por el CTS
  • Todo tipo hereda directa o indirectamente del tipo System.Object
  • Define Tipos de VALOR y de REFERENCIA

El sistema común de tipos, conocido como CTS, provee una definición común de los tipos de datos básicos que utiliza el CLR. El CTS posibilita, entre otras cosas, que todos los lenguajes de alto nivel que compilan contra una plataforma CLI compartan el mismo sistema de tipos de datos, permitiendo lograr una mejor interoperabilidad.

El CTS define como se declaran, usan y manejan en tiempos de ejecución los tipos de datos orientados a objetos que formarán el núcleo de cualquier aplicación manejada.

La Memoria y los Tipos de Datos:

  • El CLR administra dos segmentos de memoria: Stack (Pila) y Heap (Montón)
  • El Stack es liberado automáticamente y el Heap es administrado
  • Los tipos VALOR se almacenan en el Stack
  • Los tipos REFERENCIA se almacenan en el Heap

El CLR administra dos segmentos de memoria, los cuales son utilizados de distinta forma a lo largo del ciclo de vida de una aplicación:

          El Stack, o Pila: es una sección de memoria que almacena los “tipos de valor” (Value Types), llamados asi porque tanto su referencia como su valor se encuentran en la misma posición de memoria. Ejemplos de tipos por valor en el CLR son los caracteres, los números enteros y los booleanos. A estos tipos de dato también se los conoce como “tipos primitivos”. El stack se comporta como una lista LIFO (Last In – First Out), donde se van apilando valores uno encima de otro y sólo se puede recuperar un valor desapilando los que tiene por encima. La memoria ocupada por los Value Types es liberada automáticamente por el CLR una vez que se finaliza el procedimiento o el bloque de código donde fueron declarados.

          El Heap, o “Montón”: es unas sección de memoria que almacena los “tipos de referencia” (Reference Types), llamados asi porque su almacenamiento se encuentra dividido

          En el stack se almacena una referencia al contenido de la variable

          En el heap se guarda el valor propiamente dicho de la variable

Ejemplos de tipos por referencia son los Strings (cadenas de caracteres) y cualquier tipo de dato definido por el usuario (por ejemplo clases e interfaces que se creen a lo largo del desarrollo de una aplicación).

La memoria ocupada por los Reference Types es liberada automáticamente por el Garbage Collector del CLR, de manera no determinística (esto quiere decir que no se puede tener conocimiento acerca de en qué momento se liberará la memoria). El CLR no puede ser invocado por los desarrolladores, y nuca debe hacerse ninguna presuposición acerca de cuándo y cómo se ejecutará.

Comments