Qual è la differenza tra codice nativo, codice macchina e codice assembly?


106

Sono confuso sul codice macchina e sul codice nativo nel contesto dei linguaggi .NET.

Qual'è la differenza tra loro? Sono gli stessi?


3
Ho una domanda su questa domanda. Questa domanda rientra nei requisiti di StackOverflow? afaik non lo è, ma allo stesso tempo questo tipo di domanda è molto utile / informativo. Supponendo che questo tipo di domanda non sia consentito, dove dovremmo porre questo tipo di domande se non qui?
Yousuf Azad

Risposte:


150

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.


C ++ può compilare in codice macchina, ma molto spesso è compilato in altri formati come exe che funzioneranno con un sistema operativo.
Gordon Gustafson

Esistono linguaggi che supportano la garbage collection e riferimenti opachi che in genere vengono compilati in codice macchina. Le implementazioni più serie di Common Lisp lo fanno. Quello che dici potrebbe essere vero per i linguaggi supportati da Microsoft, ma ci sono più linguaggi compilati di quelli supportati da Visual Studio.
David Thornley

3
@CrazyJugglerDrummer: il codice contenuto nei file EXE generati dai compilatori C ++ è ancora codice macchina. @David Thornley: ho menzionato molte più lingue oltre a quelle, ma non volevo complicare le cose menzionando ogni oscura stranezza.
Timwi

Alcuni compilatori, molti, compileranno effettivamente da C / C ++ o altri linguaggi in linguaggio assembly quindi chiamano l'assemblatore e l'assemblatore lo trasforma in file oggetto che sono per lo più codice macchina ma necessitano di alcuni tocchi prima che possano andare in memoria sul processore quindi il linker collega tutto alla versione in codice macchina del programma. Il punto è che C / C ++, ecc. Spesso non si compila direttamente in codice macchina, è invisibile all'utente che fa due o tre passaggi lungo la strada. TCC, ad esempio, è un'eccezione a ciò che va direttamente al codice macchina.
old_timer

Sembra un pignolo, ma non tutti gli assemblatori traducono 1-1 in codici operativi. In effetti, molti assemblatori moderni supportano costrutti di astrazione come le classi. Esempio: TASM, l'assemblatore di Borland. en.wikipedia.org/wiki/TASM
Prime

45

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.


6

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.


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.