Sono confuso sul codice macchina e sul codice nativo nel contesto dei linguaggi .NET.
Qual'è la differenza tra loro? Sono gli stessi?
Sono confuso sul codice macchina e sul codice nativo nel contesto dei linguaggi .NET.
Qual'è la differenza tra loro? Sono gli stessi?
Risposte:
I termini sono davvero un po 'confusi, perché a volte sono usati in modo incoerente.
Codice macchina: questo è il più definito. È un codice che utilizza le istruzioni byte-code che il tuo processore (il pezzo di metallo fisico che fa il lavoro effettivo) comprende ed esegue direttamente. Tutto il resto del codice deve essere tradotto o trasformato in codice macchina prima che la tua macchina possa eseguirlo.
Codice nativo: questo termine viene talvolta utilizzato in luoghi in cui si intende il codice macchina (vedere sopra). Tuttavia, a volte è anche usato per indicare codice non gestito (vedi sotto).
Codice non gestito e codice gestito: il codice non gestito si riferisce al codice scritto in un linguaggio di programmazione come C o C ++, che viene compilato direttamente nel codice macchina . Contrasta con il codice gestito , scritto in C #, VB.NET, Java o simili, ed eseguito in un ambiente virtuale (come .NET o JavaVM) che "simula" un processore nel software. La differenza principale è che il codice gestito "gestisce" le risorse (principalmente l'allocazione di memoria) per te utilizzando la garbage collection e mantenendo opachi i riferimenti agli oggetti. Codice non gestitoè il tipo di codice che richiede di allocare e de-allocare manualmente la memoria, a volte causando perdite di memoria (quando ci si dimentica di disallocare) e talvolta errori di segmentazione (quando si disalloca troppo presto). Unmanaged di solito implica anche che non ci sono controlli in fase di esecuzione per errori comuni come la dereferenziazione del puntatore nullo o l'overflow dei limiti dell'array.
A rigor di termini, anche i linguaggi tipizzati dinamicamente, come Perl, Python, PHP e Ruby, sono codice gestito . Tuttavia, non sono comunemente descritti come tali, il che mostra che il codice gestito è in realtà un termine di marketing per gli ambienti di programmazione commerciali veramente grandi e seri (.NET e Java).
Codice assembly: questo termine si riferisce generalmente al tipo di codice sorgente che le persone scrivono quando vogliono veramente scrivere codice byte. Un assemblatore è un programma che trasforma questo codice sorgente in un vero byte-code. Non è un compilatore perché la trasformazione è 1 a 1. Tuttavia, il termine è ambiguo riguardo al tipo di byte-code utilizzato: potrebbe essere gestito o non gestito. Se non è gestito, il byte-code risultante è il codice macchina . Se è gestito, risulta nel codice byte utilizzato dietro le quinte da un ambiente virtuale come .NET. Il codice gestito (ad esempio C #, Java) viene compilato in questo speciale linguaggio byte-code, che nel caso di .NET è chiamato Common Intermediate Language (CIL) e in Java è chiamato Java byte-code. Di solito c'è poco bisogno che il programmatore comune acceda a questo codice o scriva direttamente in questo linguaggio, ma quando le persone lo fanno, spesso si riferiscono ad esso come codice assembly perché usano un assemblatore per trasformarlo in byte-code.
Quello che vedi quando usi Debug + Windows + Disassembly durante il debug di un programma C # è una buona guida per questi termini. Ecco una versione annotata di esso quando compilo un programma 'hello world' scritto in C # nella configurazione di rilascio con l'ottimizzazione JIT abilitata:
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
Fare clic con il pulsante destro del mouse sulla finestra e selezionare "Mostra byte di codice" per ottenere una visualizzazione simile.
La colonna a sinistra è l'indirizzo del codice macchina. Il suo valore è falsificato dal debugger, il codice si trova effettivamente da qualche altra parte. Ma potrebbe essere ovunque, a seconda della posizione selezionata dal compilatore JIT, quindi il debugger inizia a numerare gli indirizzi da 0 all'inizio del metodo.
La seconda colonna è il codice macchina . Gli 1 e gli 0 effettivi eseguiti dalla CPU. Il codice macchina, come qui, è comunemente visualizzato in esadecimale. Forse è illustrativo che 0x8B seleziona l'istruzione MOV, i byte aggiuntivi sono lì per dire alla CPU esattamente cosa deve essere spostato. Notare anche i due tipi di istruzione CALL, 0xE8 è la chiamata diretta, 0xFF è l'istruzione di chiamata indiretta.
La terza colonna è il codice assembly . L'assembly è un linguaggio semplice, progettato per semplificare la scrittura del codice macchina. Si confronta con C # compilato in IL. Il compilatore utilizzato per tradurre il codice assembly è chiamato "assembler". Probabilmente hai l'assembler Microsoft sulla tua macchina, il suo nome eseguibile è ml.exe, ml64.exe per la versione a 64 bit. Esistono due versioni comuni dei linguaggi assembly in uso. Quello che vedi è quello che Intel e AMD usano. Nel mondo open source, l'assemblaggio nella notazione AT&T è comune. La sintassi del linguaggio dipende fortemente dal tipo di CPU per cui è stato scritto, il linguaggio assembly per un PowerPC è molto diverso.
Ok, questo affronta due dei termini della tua domanda. "Codice nativo" è un termine sfocato, non è usato di rado per descrivere il codice in un linguaggio non gestito. Forse è istruttivo vedere che tipo di codice macchina viene generato da un compilatore C. Questa è la versione 'ciao mondo' in 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
Non l'ho annotato, soprattutto perché è così simile al codice macchina generato dal programma C #. La chiamata alla funzione printf () è abbastanza diversa dalla chiamata Console.WriteLine () ma tutto il resto è più o meno lo stesso. Si noti inoltre che il debugger sta ora generando l'indirizzo del codice macchina reale e che è un po 'più intelligente sui simboli. Un effetto collaterale della generazione di informazioni di debug dopo la generazione di codice macchina come spesso fanno i compilatori non gestiti. Devo anche menzionare che ho disattivato alcune opzioni di ottimizzazione del codice macchina per rendere il codice macchina simile. I compilatori C / C ++ hanno molto più tempo a disposizione per ottimizzare il codice, il risultato è spesso difficile da interpretare. E molto difficile da eseguire il debug.
Punto chiave qui è che ci sono molto poche differenze tra codice macchina generato da un linguaggio gestito dal codice compilatore JIT e la macchina generato da un compilatore di codice nativo. Questo è il motivo principale per cui il linguaggio C # può essere competitivo con un compilatore di codice nativo. L'unica vera differenza tra loro sono le chiamate alla funzione di supporto. Molti dei quali sono implementati nel CLR. E questo ruota principalmente attorno al netturbino.
Il codice nativo e il codice macchina sono la stessa cosa: i byte effettivi eseguiti dalla CPU.
Il codice assembly ha due significati: uno è il codice macchina tradotto in una forma più leggibile dall'uomo (con i byte per le istruzioni tradotti in brevi mnemonici wordlike "JMP" (che "salta" in un altro punto del codice). è il bytecode IL (byte di istruzioni generati da compilatori come C # o VB, che alla fine verranno tradotti in codice macchina, ma non lo sono ancora) che risiede in una DLL o EXE.
In .NET, gli assembly contengono codice MS Intermediate Language (MSIL, a volte CIL).
È come un codice macchina di "alto livello".
Quando viene caricato, MSIL viene compilato dal compilatore JIT in codice nativo (codice macchina Intel x86 o x64).