Je suis confus à propos du code machine et du code natif dans le contexte des langages .NET.
Quelle est la différence entre eux? Sont-ils les mêmes?
Je suis confus à propos du code machine et du code natif dans le contexte des langages .NET.
Quelle est la différence entre eux? Sont-ils les mêmes?
Réponses:
Les termes sont en effet un peu déroutants, car ils sont parfois utilisés de manière incohérente.
Code machine: c'est le plus bien défini. C'est un code qui utilise les instructions de code octet que votre processeur (le morceau de métal physique qui fait le travail réel) comprend et exécute directement. Tout autre code doit être traduit ou transformé en code machine avant que votre machine puisse l'exécuter.
Code natif: ce terme est parfois utilisé dans les endroits où le code machine (voir ci-dessus) est utilisé . Cependant, il est également parfois utilisé pour désigner du code non managé (voir ci-dessous).
Code non managé et code managé: unmanaged code fait référence à un code écrit dans un langage de programmation tel que C ou C ++, qui est compilé directement dans le code de la machine . Il contraste avec le code managé , qui est écrit en C #, VB.NET, Java ou similaire, et exécuté dans un environnement virtuel (tel que .NET ou JavaVM) qui «simule» en quelque sorte un processeur dans un logiciel. La principale différence est que le code managé «gère» les ressources (principalement l'allocation de mémoire) pour vous en utilisant le ramasse-miettes et en gardant les références aux objets opaques. Code non géréest le type de code qui vous oblige à allouer et désallouer manuellement de la mémoire, provoquant parfois des fuites de mémoire (lorsque vous oubliez de désallouer) et parfois des erreurs de segmentation (lorsque vous désallouez trop tôt). Unmanaged implique également généralement qu'il n'y a pas de vérifications au moment de l'exécution pour les erreurs courantes telles que le déréférencement du pointeur nul ou le dépassement des limites du tableau.
À proprement parler, la plupart des langages typés dynamiquement - tels que Perl, Python, PHP et Ruby - sont également du code géré . Cependant, ils ne sont pas généralement décrits comme tels, ce qui montre que le code managé est en fait un terme marketing pour désigner les environnements de programmation commerciaux très importants et sérieux (.NET et Java).
Code d'assemblage: ce terme fait généralement référence au type de code source que les gens écrivent lorsqu'ils veulent vraiment écrire du code octet. Un assembleur est un programme qui transforme ce code source en véritable byte-code. Ce n'est pas un compilateur car la transformation est 1 vers 1. Cependant, le terme est ambigu quant au type d'octet-code utilisé: il peut être géré ou non. S'il n'est pas géré, l'octet-code résultant est le code machine . S'il est géré, il en résulte le code d'octet utilisé en arrière-plan par un environnement virtuel tel que .NET. Le code managé (par exemple C #, Java) est compilé dans ce langage spécial d'octet-code, qui dans le cas de .NET est appelé Common Intermediate Language (CIL) et en Java est appelé Java byte-code. Il est généralement peu nécessaire que le programmeur commun accède à ce code ou écrive directement dans ce langage, mais lorsque les gens le font, ils y font souvent référence en tant que code d'assemblage car ils utilisent un assembleur pour le transformer en byte-code.
Ce que vous voyez lorsque vous utilisez Debug + Windows + Disassembly lors du débogage d'un programme C # est un bon guide pour ces termes. Voici une version annotée de celui-ci lorsque je compile un programme `` hello world '' écrit en C # dans la configuration Release avec l'optimisation JIT activée:
static void Main(string[] args) {
Console.WriteLine("Hello world");
00000000 55 push ebp ; save stack frame pointer
00000001 8B EC mov ebp,esp ; setup current frame
00000003 E8 30 BE 03 6F call 6F03BE38 ; Console.Out property getter
00000008 8B C8 mov ecx,eax ; setup "this"
0000000a 8B 15 88 20 BD 02 mov edx,dword ptr ds:[02BD2088h] ; arg = "Hello world"
00000010 8B 01 mov eax,dword ptr [ecx] ; TextWriter reference
00000012 FF 90 D8 00 00 00 call dword ptr [eax+000000D8h] ; TextWriter.WriteLine()
00000018 5D pop ebp ; restore stack frame pointer
}
00000019 C3 ret ; done, return
Cliquez avec le bouton droit sur la fenêtre et cochez "Afficher les octets de code" pour obtenir un affichage similaire.
La colonne de gauche correspond à l'adresse du code machine. Sa valeur est truquée par le débogueur, le code est en fait situé ailleurs. Mais cela peut être n'importe où, en fonction de l'emplacement sélectionné par le compilateur JIT, de sorte que le débogueur commence simplement à numéroter les adresses à partir de 0 au début de la méthode.
La deuxième colonne est le code machine . Les 1 et 0 réels exécutés par la CPU. Le code machine, comme ici, est généralement affiché en hexadécimal. Peut-être que 0x8B sélectionne l'instruction MOV, les octets supplémentaires sont là pour indiquer exactement au CPU ce qui doit être déplacé. Notez également les deux saveurs de l'instruction CALL, 0xE8 est l'appel direct, 0xFF est l'instruction d'appel indirect.
La troisième colonne est le code d'assemblage . L'assembly est un langage simple, conçu pour faciliter l'écriture de code machine. Il se compare à C # en cours de compilation en IL. Le compilateur utilisé pour traduire le code d'assembly est appelé un «assembleur». Vous avez probablement l'assembleur Microsoft sur votre machine, son nom d'exécutable est ml.exe, ml64.exe pour la version 64 bits. Il existe deux versions courantes des langages d'assemblage en cours d'utilisation. Celui que vous voyez est celui qu'utilisent Intel et AMD. Dans le monde open source, l'assemblage dans la notation AT&T est courant. La syntaxe du langage dépend fortement du type de CPU pour lequel il a été écrit, le langage d'assemblage d'un PowerPC est très différent.
D'accord, cela aborde deux des termes de votre question. «Code natif» est un terme flou, il n'est pas rarement utilisé pour décrire du code dans un langage non managé. Il est peut-être instructif de voir quel type de code machine est généré par un compilateur C. Voici la version «bonjour le monde» en C:
int _tmain(int argc, _TCHAR* argv[])
{
00401010 55 push ebp
00401011 8B EC mov ebp,esp
printf("Hello world");
00401013 68 6C 6C 45 00 push offset ___xt_z+128h (456C6Ch)
00401018 E8 13 00 00 00 call printf (401030h)
0040101D 83 C4 04 add esp,4
return 0;
00401020 33 C0 xor eax,eax
}
00401022 5D pop ebp
00401023 C3 ret
Je ne l'ai pas annoté, principalement parce qu'il est tellement similaire au code machine généré par le programme C #. L'appel de la fonction printf () est assez différent de l'appel de Console.WriteLine () mais tout le reste est à peu près identique. Notez également que le débogueur génère maintenant l'adresse de code machine réelle et qu'il est un peu plus intelligent sur les symboles. Un effet secondaire de la génération d'informations de débogage après la génération de code machine comme le font souvent les compilateurs non gérés. Je dois également mentionner que j'ai désactivé quelques options d'optimisation du code machine pour rendre le code machine similaire. Les compilateurs C / C ++ ont beaucoup plus de temps disponible pour optimiser le code, le résultat est souvent difficile à interpréter. Et très difficile à déboguer.
Le point clé ici est qu'il existe très peu de différences entre le code machine généré à partir d'un langage managé par le compilateur JIT et le code machine généré par un compilateur de code natif. C'est la principale raison pour laquelle le langage C # peut être compétitif avec un compilateur de code natif. La seule vraie différence entre eux réside dans les appels de fonction de support. Beaucoup d'entre eux sont mis en œuvre dans le CLR. Et cela tourne principalement autour du ramasse-miettes.
Le code natif et le code machine sont la même chose - les octets réels exécutés par le CPU.
Le code d'assemblage a deux significations: l'une est le code machine traduit dans une forme plus lisible par l'homme (avec les octets des instructions traduits en de courts mnémoniques de type mot comme "JMP" (qui "saute" à un autre endroit du code). L'autre est le bytecode IL (octets d'instructions générés par des compilateurs comme C # ou VB, qui finiront par être traduits en code machine, mais ne le sont pas encore) qui se trouvent dans une DLL ou un EXE.
Dans .NET, les assemblys contiennent du code MS Intermediate Language (MSIL, parfois CIL).
C'est comme un code machine «de haut niveau».
Une fois chargé, MSIL est compilé par le compilateur JIT en code natif (code machine Intel x86 ou x64).