Compilar MSIL a código nativo
Actualización: noviembre 2007
Para poder ejecutar el lenguaje intermedio de Microsoft (MSIL), primero debe compilarse a código nativo con Common Language Runtime para la arquitectura del equipo de destino. .NET Framework proporciona dos mecanismos para realizar esta conversión:
Un compilador Just-In-Time (JIT) de .NET Framework.
La herramienta Generador de imágenes nativas (Ngen.exe) de .NET Framework.
Realizar la compilación con el compilador Just-In-Time (JIT)
La compilación JIT convierte MSIL en código nativo a petición en el tiempo de ejecución de la aplicación, cuando el contenido de un ensamblado se carga y ejecuta. Common Language Runtime proporciona un compilador JIT para cada arquitectura de CPU compatible, por lo que los programadores pueden crear un conjunto de ensamblados MSIL que se puede compilar con un compilador JIT y se puede ejecutar en equipos distintos con diferentes arquitecturas. No obstante, el código administrado sólo se ejecutará en un determinado sistema operativo si llama a las API nativas específicas de la plataforma o a una biblioteca de clases específica de la plataforma.
La compilación JIT tiene en cuenta el hecho de que durante la ejecución nunca se llamará a parte del código. En vez de utilizar tiempo y memoria para convertir todo el MSIL de un archivo ejecutable portable (PE) a código nativo, convierte el MSIL necesario durante la ejecución y almacena el código nativo resultante en memoria para que sea accesible en las llamadas posteriores que se produzcan en el contexto de ese proceso. El cargador crea y asocia un código auxiliar a cada método de un tipo cuando este tipo se carga y se inicializa. Cuando se llama a un método por primera vez, el código auxiliar pasa el control al compilador JIT, el cual convierte el MSIL del método en código nativo y modifica el código auxiliar para señalar directamente al código nativo generado. Por tanto, las siguientes llamadas al método compilado mediante un compilador JIT pasan directamente al código nativo.
Generación de código en tiempo de instalación mediante NGen.exe
Dado que el compilador JIT convierte el MSIL de un ensamblado en código nativo cuando se llama a cada uno de los métodos definidos en ese ensamblado, esto tiene necesariamente un impacto sobre el rendimiento en tiempo de ejecución. En la mayoría de los casos, este impacto en el rendimiento es aceptable. Y lo que es más importante, el código generado por el compilador JIT se enlaza al proceso que activó la compilación. Este código no se puede compartir en varios procesos. Para permitir que el código generado se comparta en varias invocaciones de una aplicación o en varios procesos que compartan un conjunto de ensamblados, Common Language Runtime admite un modo de compilación anticipado. Este modo de compilación anticipado utiliza Generador de imágenes nativas (Ngen.exe) para convertir los ensamblados MSIL en código nativo de forma muy similar a como lo hace el compilador JIT. Sin embargo, la operación de Ngen.exe se diferencia de la del compilador JIT en tres aspectos:
Realiza la conversión de MSIL a código nativo antes de ejecutar la aplicación en lugar de realizarla durante su ejecución.
Compila a la vez un ensamblado completo, en lugar de compilar un método cada vez.
Conserva el código generado en la memoria caché de imágenes nativas como un archivo en disco.
Comprobación del código
Como parte de la compilación MSIL en código nativo, el código de MILS debe pasar un proceso de comprobación, a menos que el administrador haya establecido una directiva de seguridad que permita al código omitir esta comprobación. En esta comprobación se examina el MSIL y los metadatos para determinar si el código garantiza la seguridad de tipos, lo que significa que el código sólo tiene acceso a aquellas ubicaciones de la memoria para las que está autorizado. La seguridad de tipos ayuda a aislar los objetos entre sí y, por tanto, ayuda a protegerlos contra daños involuntarios o maliciosos. Además, garantiza que las restricciones de seguridad sobre el código se aplican con toda certeza.
El motor en tiempo de ejecución se basa en el hecho de que se cumplan las siguientes condiciones para el código seguro comprobable:
La referencia a un tipo es estrictamente compatible con el tipo al que hace referencia.
En un objeto sólo se invocan las operaciones definidas adecuadamente.
Una identidad es precisamente lo que dice ser.
Durante el proceso de comprobación, se examina el código MSIL para intentar confirmar que el código tiene acceso a las ubicaciones de memoria y puede llamar a los métodos sólo a través de los tipos definidos correctamente. Por ejemplo, un código no permite el acceso a los campos de un objeto si esta acción sobrecarga las ubicaciones de memoria. Además, el proceso de comprobación examina el código para determinar si el MSIL se ha generado correctamente, ya que un MSIL incorrecto puede llevar a la infracción de las reglas en materia de seguridad de tipos. El proceso de comprobación pasa un conjunto de código seguro bien definido, y pasa exclusivamente código seguro. No obstante, algunos códigos con seguridad de tipos no pasan la comprobación debido a algunas limitaciones de este proceso, y algunos lenguajes no producen código seguro comprobable debido a su diseño. Si la directiva de seguridad requiere código seguro de tipos y el código no pasa la comprobación, se produce una excepción al ejecutar el código.