Partager via


Processus d’exécution managée

Le processus d’exécution managée inclut les étapes suivantes, qui sont décrites en détail plus loin dans cette rubrique :

  1. Choix d’un compilateur. Pour obtenir les avantages fournis par le Common Language Runtime, vous devez utiliser un ou plusieurs compilateurs de langage qui ciblent le runtime.
  2. Compilation de votre code en langage intermédiaire. La compilation traduit votre code source en langage intermédiaire commun (CIL) et génère les métadonnées requises.
  3. Compilation de CIL en code natif. Au moment de l’exécution, un compilateur juste-à-temps (JIT) traduit le code CIL en code natif. Pendant cette compilation, le code doit passer un processus de vérification qui examine la liste CIL et les métadonnées pour déterminer si le code peut être déterminé comme étant sécurisé.
  4. Exécution du code. Le Common Language Runtime fournit l’infrastructure qui permet l’exécution et les services qui peuvent être utilisés pendant l’exécution.

Choisir un compilateur

Pour obtenir les avantages fournis par le Common Language Runtime (CLR), vous devez utiliser un ou plusieurs compilateurs de langage qui ciblent le runtime, tels que Visual Basic, C#, Visual C++, F# ou l’un de nombreux compilateurs tiers tels qu’un compilateur Eiffel, Perl ou COBOL.

Étant donné qu’il s’agit d’un environnement d’exécution multi-anguage, le runtime prend en charge un large éventail de types de données et de fonctionnalités de langage. Le compilateur de langage que vous utilisez détermine les fonctionnalités d’exécution disponibles et vous concevez votre code à l’aide de ces fonctionnalités. Votre compilateur, et non le runtime, établit la syntaxe que votre code doit utiliser. Si votre composant doit être entièrement utilisable par les composants écrits dans d’autres langages, les types exportés de votre composant doivent exposer uniquement les fonctionnalités de langage incluses dans la spécification CLS (Common Language Specification). Vous pouvez utiliser l’attribut CLSCompliantAttribute pour vous assurer que votre code est conforme à CLS. Pour plus d’informations, consultez Les composants indépendants de la langue et de l’indépendance du langage.

Compiler dans CIL

Lors de la compilation en code managé, le compilateur traduit votre code source en langage intermédiaire commun (CIL), qui est un ensemble d’instructions indépendant du processeur qui peut être converti efficacement en code natif. CIL inclut des instructions pour le chargement, le stockage, l’initialisation et l’appel de méthodes sur des objets, ainsi que des instructions pour les opérations arithmétiques et logiques, le flux de contrôle, l’accès direct à la mémoire, la gestion des exceptions et d’autres opérations. Avant de pouvoir exécuter le code, CIL doit être converti en code spécifique au processeur, généralement par un compilateur juste-à-temps (JIT). Étant donné que le Common Language Runtime fournit un ou plusieurs compilateurs JIT pour chaque architecture d’ordinateur qu’il prend en charge, le même ensemble de CIL peut être compilé par JIT et s’exécuter sur n’importe quelle architecture prise en charge.

Lorsqu’un compilateur produit CIL, il produit également des métadonnées. Les métadonnées décrivent les types dans votre code, notamment la définition de chaque type, les signatures des membres de chaque type, les membres référencés par votre code et d’autres données que le runtime utilise au moment de l’exécution. La bibliothèque CIL et les métadonnées sont contenues dans un fichier exécutable portable (PE) basé sur et qui étend le format de fichier d’objet commun (COFF) Microsoft PE publié utilisé historiquement pour le contenu exécutable. Ce format de fichier, qui prend en charge le code CIL ou natif ainsi que les métadonnées, permet au système d’exploitation de reconnaître les images common language runtime. La présence de métadonnées dans le fichier avec CIL permet à votre code de se décrire, ce qui signifie qu’il n’est pas nécessaire de bibliothèques de types ou d’IDL (Interface Definition Language). Le runtime localise et extrait les métadonnées du fichier selon les besoins pendant l’exécution.

Compiler CIL en code natif

Avant de pouvoir exécuter le langage intermédiaire commun (CIL), il doit être compilé sur le Common Language Runtime en code natif pour l’architecture de l’ordinateur cible. .NET fournit deux façons d’effectuer cette conversion :

Compilation par le compilateur JIT

La compilation JIT convertit CIL en code natif à la demande au moment de l’exécution de l’application, lorsque le contenu d’un assembly est chargé et exécuté. Étant donné que le Common Language Runtime fournit un compilateur JIT pour chaque architecture de processeur prise en charge, les développeurs peuvent créer un ensemble d’assemblys CIL qui peuvent être compilés et exécutés sur différents ordinateurs avec différentes architectures d’ordinateur. Toutefois, si votre code managé appelle des API natives spécifiques à la plateforme ou une bibliothèque de classes spécifique à la plateforme, elle s’exécute uniquement sur ce système d’exploitation.

La compilation JIT prend en compte la possibilité que du code ne soit jamais appelé pendant l’exécution. Au lieu d’utiliser du temps et de la mémoire pour convertir toutes les valeurs CIL d’un fichier PE en code natif, il convertit le code CIL en fonction des besoins pendant l’exécution et stocke le code natif résultant en mémoire afin qu’il soit accessible pour les appels suivants dans le contexte de ce processus. Le chargeur crée et attache un stub à chaque méthode dans un type lorsque le type est chargé et initialisé. Lorsqu’une méthode est appelée pour la première fois, le stub passe le contrôle au compilateur JIT, qui convertit le code CIL pour cette méthode en code natif et modifie le stub pour pointer directement vers le code natif généré. Par conséquent, les appels suivants à la méthode compilée par JIT vont directement au code natif.

Génération de code au moment de l’installation à l’aide de NGen.exe

Étant donné que le compilateur JIT convertit le code CIL d’un assembly en code natif lorsque des méthodes individuelles définies dans cet assembly sont appelées, cela affecte les performances négativement au moment de l’exécution. Dans la plupart des cas, les performances réduites sont acceptables. Plus important encore, le code généré par le compilateur JIT est lié au processus qui a déclenché la compilation. Il ne peut pas être partagé entre plusieurs processus. Pour permettre au code généré d’être partagé entre plusieurs appels d’une application ou sur plusieurs processus qui partagent un ensemble d’assemblys, le Common Language Runtime prend en charge un mode de compilation à l’avance. Ce mode de compilation à l’avance utilise le Ngen.exe (Générateur d’images natives) pour convertir des assemblys CIL en code natif comme le fait le compilateur JIT. Toutefois, l’opération de Ngen.exe diffère de celle du compilateur JIT de trois façons :

  • Il effectue la conversion de CIL en code natif avant d’exécuter l’application au lieu de l’exécution de l’application pendant l’exécution de l’application.
  • Il compile un assembly entier à la fois, au lieu d’une méthode à la fois.
  • Il conserve le code généré dans le cache d’images natives en tant que fichier sur le disque.

Vérification du code

Dans le cadre de sa compilation en code natif, le code CIL doit passer un processus de vérification, sauf si un administrateur a établi une stratégie de sécurité qui permet au code de contourner la vérification. La vérification examine CIL et les métadonnées pour déterminer si le code est de type sécurisé, ce qui signifie qu’il accède uniquement aux emplacements de mémoire auxquels il est autorisé à accéder. La sécurité des types permet d’isoler les objets les uns des autres et de les protéger contre une corruption accidentelle ou malveillante. Il garantit également que les restrictions de sécurité sur le code peuvent être appliquées de manière fiable.

Le runtime s’appuie sur le fait que les instructions suivantes sont vraies pour le code qui est un type vérifiable sécurisé :

  • Une référence à un type est strictement compatible avec le type référencé.
  • Seules les opérations définies de manière appropriée sont appelées sur un objet.
  • Les identités sont ce qu’elles prétendent être.

Pendant le processus de vérification, le code CIL est examiné dans une tentative de confirmer que le code peut accéder aux emplacements de mémoire et appeler des méthodes uniquement par le biais de types correctement définis. Par exemple, le code ne peut pas autoriser l’accès aux champs d’un objet d’une manière qui permet aux emplacements de mémoire d’être dépassés. En outre, la vérification inspecte le code pour déterminer si la liste CIL a été correctement générée, car la valeur CIL incorrecte peut entraîner une violation des règles de sécurité de type. Le processus de vérification transmet un ensemble bien défini de code de type sécurisé, et il transmet uniquement le code qui est de type sécurisé. Toutefois, certains codes de type sécurisé peuvent ne pas réussir la vérification en raison de certaines limitations du processus de vérification, et certains langages, par conception, ne produisent pas de code vérifiable de type sécurisé. Si le code de type sécurisé est requis par la stratégie de sécurité, mais que le code ne passe pas la vérification, une exception est levée lorsque le code est exécuté.

Exécuter le code

Le Common Language Runtime fournit l’infrastructure qui permet l’exécution managée et les services qui peuvent être utilisés pendant l’exécution. Pour qu’une méthode puisse être exécutée, elle doit être compilée en code spécifique au processeur. Chaque méthode pour laquelle CIL a été générée est compilée par JIT lorsqu’elle est appelée pour la première fois, puis exécutée. La prochaine fois que la méthode est exécutée, le code natif compilé par JIT existant est exécuté. Le processus de compilation JIT, puis l’exécution du code est répété jusqu’à ce que l’exécution soit terminée.

Pendant l’exécution, le code managé reçoit des services tels que le garbage collection, la sécurité, l’interopérabilité avec du code non managé, la prise en charge du débogage inter-langages et la prise en charge améliorée du déploiement et du contrôle de version.

Dans Microsoft Windows Vista, le chargeur de système d’exploitation vérifie les modules managés en examinant un peu l’en-tête COFF. Le bit en cours de définition désigne un module managé. Si le chargeur détecte les modules managés, il charge mscoree.dll, et _CorValidateImage_CorImageUnloading avertit le chargeur lorsque les images de module managé sont chargées et déchargées. _CorValidateImage effectue les actions suivantes :

  1. Garantit que le code est un code managé valide.
  2. Remplace le point d’entrée de l’image par un point d’entrée dans le runtime.

Sur Windows 64 bits, _CorValidateImage modifie l’image en mémoire en la transformant du format PE32 au format PE32+.

Voir aussi