miércoles, 1 de octubre de 2008

Proceso de compilación en .Net Framewrok

Un compilador es la pieza base a la hora de desarrollar un programa. Hay compiladores para todos los lenguajes y su función es traducir un programa escrito en un determinado lenguaje a un idioma que el computador entienda. De esta manera el compilador se convierte en un traductor, el cual es un programa que toma como entrada un texto escrito en un lenguaje, llamado fuente y da como salida otro texto en un lenguaje, denominado objeto.

Compilación a código intermedio: Este tipo de compilación es la encargada de traducir desde el código fuente del programador a un lenguaje intermedio independiente de la plataforma.
Compilación JIT (Just In Time) a código nativo: En este paso, el código intermedio es traducido a código nativo específico de cada plataforma.
La función de este tipo de compiladores es traducir el código que el programador escribe en cualquiera de los lenguajes soportados por el CLR (Common Language Runtime) a un Lenguaje Intermedio estándar (LI). Este lenguaje es un lenguaje independiente de la plataforma.

Escrito el código fuente, las diferencias de implementación desaparecen en el momento en que entra en funcionamiento el CLR. Para que no haya ningún problema en la interacción de lenguajes, los distintos tipos de lenguajes tienen que seguir unas pautas establecidas en él.

Cuando un compilador genera código intermedio, también genera a su vez metadatos. Los metadatos es información que el CLR va a utilizar a en tiempo de ejecución, como puede ser, por ejemplo, la definición de tipos que hay en el código. Estos metadatos, junto con el código intermedio se almacenan en un fichero PE (Portable Executable). Una de las ventajas de este tipo de ejecutable es que al almacenar los metadatos y el código intermedio juntos, el código se describe así mismo, con lo cual ya no es necesario, por ejemplo tener IDLs (Interface Definition Language).

Todas las herramientas de desarrollo generan el mismo IL, independientemente del lenguaje en el que se instala junto con el Framework. Esta herramienta nos permite, entre otras cosas, ver cómo está formado este tipo de ficheros, ver qué código intermedio ha generado cada función escrita y muchas otras cosas interesantes. Podemos encontrar un tutorial dentro de la documentación del Framework.

La especificación de este IL es abierta, con lo cual cualquiera que lo desee puede construir su propio compilador a código intermedio compatible con el CLR.
Una vez que se ha realizado un programa y se ha compilado con los compiladores que ofrece la plataforma, el código resultante no se puede ejecutar directamente sobre la máquina, ya que lo que contienen es código intermedio. Para poder ser ejecutados, necesitan previamente pasar por un compilador JIT.
En primer lugar, aclarar lo que significa JIT. Estas siglas pertenecen a Just-In-Time, nombre que recibe este tipo de compilación debido a que se realiza en tiempo de ejecución.

Antes de que el código intermedio anteriormente generado sea ejecutado, debe ser convertido por el JIT en código nativo, esto es, código que pueda entender el computador. Este código es específico para cada arquitectura y lo que le proporciona a .NET independencia de la plataforma, con lo cual, nosotros escribimos el mismo código independientemente de la arquitectura en la que se vaya a ejecutar.

No obstante esto no es siempre necesario, ya que para cierto tipo de aplicaciones, puede resultar ineficiente y hay opciones que permiten que la compilación JIT sólo se realice una única vez cuando la aplicación es instalada en la máquina (compilación en tiempo de instalación) y el código nativo quede almacenado en el sistema, al igual que ocurre con las aplicaciones no pertenecientes a .NET. Esto se hace mediante la herramienta Ngen.exe, en línea de comandos y es el generador de imágenes nativas.

Una de las ventajas de la compilación just-in-time es que no compila todo nada más llamar al ejecutable, esto es, en vez de convertir todo el fichero PE (ver Compiladores de Lenguaje Intermedio) a código nativo, va convirtiendo el código intermedio que vaya necesitando, ahorrando así tiempo y recursos. Además, el código que ya se ha traducido, es almacenado para llamadas posteriores.

En la etapa de generación de código nativo a partir de código intermedio, también tiene lugar un proceso de verificación (siempre va a suceder a menos que el administrador del sistema indique lo contrario). La verificación comprueba el código intermedio y los metadatos para comprobar si el código puede ser considerado como “seguro” (accede únicamente a aquellas direcciones de memoria que puede acceder). No obstante, no todos los lenguajes soportan la generación de código seguro (por ejemplo C++ con los punteros). Si las políticas de sistema requieren la ejecución de código seguro, se lanzará una excepción a la hora de ejecutar dicho código.




Escrito por:
Evelin Guerra Pamplona
MSP-República Dominicana

Descargar artículo

No hay comentarios: