Perché le VM devono essere "macchine stack" o "macchine registratrici" ecc.?


48

(Questa è una domanda estremamente nuova).

Ho studiato un po 'di macchine virtuali.

Si scopre che molti di questi sono progettati in modo molto simile ai computer fisici o teorici.

Ho letto che la JVM, ad esempio, è una "macchina dello stack". Ciò significa (e correggimi se sbaglio) è che memorizza tutta la sua "memoria temporanea" su uno stack e fa operazioni su questo stack per tutti i suoi codici operativi.

Ad esempio, il codice sorgente 2 + 3verrà tradotto in bytecode simile a:

push 2
push 3
add

La mia domanda è questa:

Le JVM sono probabilmente scritte usando C / C ++ e simili. In tal caso, perché JVM non esegue il seguente codice C: 2 + 3..? Voglio dire, perché ha bisogno di uno stack o di altri "registri" di macchine virtuali, come in un computer fisico?

La CPU fisica sottostante si occupa di tutto ciò. Perché i writer VM non eseguono semplicemente il bytecode interpretato con le istruzioni "normali" nella lingua con cui è programmata la VM?

Perché le macchine virtuali devono emulare l'hardware, quando l'hardware effettivo già lo fa per noi?

Ancora una volta, domande per principianti. Grazie per l'aiuto


5
Hai considerato su cosa si basano le macchine non virtuali?

5
@MichaelT Intendi macchine fisiche?
Aviv Cohn,

Naturalmente, la maggior parte delle macchine virtuali Javascript non sono macchine stack o macchine di registrazione - V8 / IonMonkey / Chakra / ecc. Sono macchine virtuali che implementano Javascript. Una "VM" è solo un interprete o un compilatore JIT che può implementare qualsiasi linguaggio desideri dal designer.
Billy ONeal,

@BillyONeal Quindi, ad esempio, se sto scrivendo una VM per un po 'di lingua e la sto scrivendo in C: la VM analizza la riga bytcode' print "ciao" 'ed esegue printf("hi");: è considerata una VM? Non ha "stack" o "registri" e quant'altro.
Aviv Cohn,

@Prog: Sì, è corretto.
Billy ONeal,

Risposte:


51

Una macchina, virtuale o no, ha bisogno di un modello di calcolo che descriva come viene eseguito il calcolo su di essa. Per definizione, non appena calcola, implementa alcuni modelli di calcolo. La domanda allora è: quale modello dovremmo scegliere per la nostra VM? Le macchine fisiche sono vincolate da ciò che può essere fatto in modo efficace ed efficiente nell'hardware. Ma, come notate, le macchine virtuali non hanno tali vincoli, sono definite nel software usando linguaggi di livello arbitrariamente alto.

Esistono, infatti, macchine virtuali di alto livello come da te descritto. Si chiamano linguaggi di programmazione . Lo standard C, ad esempio, dedica la maggior parte delle sue pagine alla definizione di un modello per la cosiddetta "macchina astratta C" che descrive il comportamento dei programmi C e, per estensione (regola as-if), come un compilatore (o interprete) C conforme dovrebbe comportarsi.

Naturalmente, di solito non la chiamiamo una macchina virtuale. Una VM di solito significa qualcosa di livello inferiore, più vicino all'hardware, non destinato a essere programmato direttamente, progettato per essere eseguito in modo efficiente. Questo errore di selezione significa che qualcosa che accetta codice componibile di alto livello (come quello che descrivi) non verrebbe considerato una macchina virtuale perché esegue un codice di alto livello.

Ma per arrivare al punto, ecco alcuni motivi per creare una macchina virtuale (come in, qualcosa di mirato da un compilatore bytecode) basata su registro o simili. Le macchine stack e register sono estremamente semplici. C'è una sequenza di istruzioni, alcuni stati e semantiche per ogni istruzione (una funzione State -> State). Nessuna riduzione complessa dell'albero, nessuna precedenza per l'operatore. Analizzarlo, analizzarlo ed eseguirlo è molto semplice, perché è un linguaggio minimale (lo zucchero sintattico è compilato via) e progettato per essere letto automaticamente piuttosto che umano.

Al contrario, analizzare anche i linguaggi simil-C più semplici è piuttosto difficile ed eseguirlo richiede analisi non locali come il controllo e la propagazione dei tipi, la risoluzione di sovraccarichi, la manutenzione di una tabella di simboli, la risoluzione di identificatori di stringhe , la trasformazione di testo lineare in un AST basato sulla precedenza , e così via. Si basa su concetti che diventano naturali per l'uomo ma devono essere accuratamente ingegnerizzati dalle macchine.

Il bytecode JVM, ad esempio, viene emesso da javac. Non ha praticamente mai bisogno di essere letto o scritto dagli umani, quindi è naturale orientarlo verso il consumo da parte delle macchine. Se ottimizzato per gli esseri umani, la JVM sarebbe solo ad ogni avvio di leggere il codice, analizzarlo, analizzare è, e quindi convertirlo in una rappresentazione intermedia simile ad una semplificata tale modello di macchina in ogni caso . Potrebbe anche tagliare l'uomo di mezzo.


Quindi quello che stai dicendo è che la compilazione di tutto in istruzioni nello stack (ovvero System.out.println("hi");è compilata in alcune istruzioni in uno stack, int a = 7è compilata in un'istruzione in stack, ecc.) Rende l'esecuzione del programma semplice ed efficiente?
Aviv Cohn,

2
@Prog Fondamentalmente sì. Ma non solo esecuzione, anche analisi. Tutto ciò che viene fatto a livello di codice.

Tuttavia, non capisco perché 2 + 3venga compilato push 2 push 3 add. Il addpasso alla fine viene eseguito dalla JVM comunque eseguendo il codice C 2 + 3. Non c'è altro modo per i programmatori della JVM di farlo. Perché non compilarlo 2 + 3e fare in modo che JVM esegua il codice C 2 + 3(supponendo che sia scritto in C) immediatamente?
Aviv Cohn,

@Prog L'autore JVM non può semplicemente scrivere 2 + 3nel codice sorgente JVM perché JVM deve lavorare con qualsiasi programma eseguendo operazioni in qualsiasi ordine. La creazione del codice sorgente C e il rinvio a un'implementazione C non fa altro che inserire lo stesso problema nell'implementazione C (e non può essere fatto facilmente, per non parlare dell'efficienza). Ci deve essere una struttura di dati che descriva il programma, in modo che possa essere interpretato e compilato JIT, e il "codice sorgente leggibile dall'uomo" è una scelta terribile della struttura di dati per i motivi sopra descritti.

7
@Prog Sembri troppo concentrato sul caso specifico di 2 + 3. Che dire a + b? Quindi i valori da aggiungere non provengono i.argument{1,2}, vengono caricati dalle variabili locali. Che dire frobnicate(x[i]) + (Foo.bar() * 2)? Usando questo disegno, c'è una sola addoperazione (per int) e funziona indipendentemente da come sono stati calcolati i componenti aggiuntivi. Inoltre, un'istruzione che aggiunge solo valori letterali interi sarebbe inutile: il suo risultato potrebbe anche essere pre-calcolato (cioè invece add(2,3)che dovrebbe essere push(5)).

20

Questa risposta si concentra sulla JVM, ma in realtà si applica a qualsiasi VM.

Perché le macchine virtuali devono emulare l'hardware, quando l'hardware effettivo già lo fa per noi?

Non lo fanno, ma rende la VM molto più semplice e portatile: una VM che emula l'hardware può utilizzare lo stesso modello computazionale di qualsiasi CPU hardware.

La JVM in particolare è stata costruita pensando alla portabilità, infatti è stata costruita in modo da poter essere implementata anche nell'hardware (potrebbe essere difficile da credere oggi, ma l' origine di Java era nel mondo incorporato - in particolare, i controller per la televisione interattiva ).

Se si dispone di un obiettivo come questo, è auspicabile che la VM operi il più vicino possibile a una macchina fisica, poiché la traduzione nel codice macchina effettivo diventa più semplice e quindi più rapida. Una volta che hai gli opcode della VM, in teoria, tutto ciò che devi fare è tradurre in opcode della CPU su cui il programma viene effettivamente eseguito. In pratica non è esattamente così semplice.

Voglio dire, perché ha bisogno di uno stack o di altri "registri" di macchine virtuali, come in un computer fisico?

L'uso di un modello di macchina virtuale basato su stack ha il vantaggio di poter essere facilmente trasferito su macchine sia di registro che di stack, mentre non è necessariamente vero il contrario. Una VM basata su registri dovrebbe formulare ipotesi sul numero di registri, sulla dimensione dei registri, ecc. Con una macchina stack non sono necessari tali presupposti.

La CPU fisica sottostante si occupa di tutto ciò. Perché i writer VM non eseguono semplicemente il bytecode interpretato con le istruzioni "normali" nella lingua con cui è programmata la VM?

Bene, questo è ciò che fanno queste macchine virtuali, interpretano per codice. Anche la JVM in realtà lo fa, almeno prima che JIT (just-in-time) entri in azione: interpreta i codici byte ed esegue le istruzioni nella lingua in cui è stata scritta la JVM (in genere C o C ++, ma ne esiste anche una scritta in JavaScript, Doppio ). Si noti, tuttavia, che anche tali dichiarazioni sono state tradotte in codice macchina da un compilatore e in realtà sembrano molto simili a quelle prodotte dal compilatore Java, ovvero usano registri e stack per eseguire il loro lavoro. Si noti che l'uso di linguaggi "interpretati" vs "compilati" diventa piuttosto sfocato a questo punto.


Naturalmente, tutto ciò che può essere implementato nel software può essere implementato nell'hardware. Inoltre, la JVM attualmente (hotspot) è un compilatore JIT - non esegue le istruzioni nella lingua in cui è stata scritta la JVM. In tal caso, Java si comporterebbe terribilmente e non sarebbe affatto vicino a una piattaforma praticabile come lo è oggi . (Al diavolo, la maggior parte delle implementazioni di Javascript sarebbero più veloci)
Billy ONeal

2
@BillyONeal "Piuttosto che compilare metodo per metodo, giusto in tempo, la VM HotSpot Java esegue immediatamente il programma usando un interprete e analizza il codice mentre viene eseguito per rilevare i punti critici nel programma. Quindi focalizza l'attenzione di un ottimizzatore globale di codice nativo sui punti di interesse. "citato da oracle.com/technetwork/java/whitepaper-135217.html#2 , sezione" Rilevamento di hot spot "
miraculixx

Sì. "Native code optimizer" == compilazione JIT. C'è una fase dell'interprete per il codice che non sembra essere "caldo" per evitare che JIT usi cose raramente usate. Ma ciò non significa che non si esegua JITing.
Billy ONeal,

Grazie per aver risposto. Ciò che ho raccolto dalla tua risposta è che i motivi per emulare l'hardware nella VM (ovvero con "stack" o "registri" ecc.) È perché semplifica la successiva compilazione del bytecode o del codice sorgente nell'effettivo codice macchina di un CPU fisica. Tuttavia, a parte questo, c'è qualcosa da guadagnare dall'emulazione dell'hardware in una VM? Non capisco ancora perché qualcuno che progetta una VM dovrebbe pensare in termini di "macchina stack" o "macchina di registrazione" ecc. Quando in realtà stiamo parlando di software. Mi sto perdendo qualcosa?
Aviv Cohn,

@Prog Ok, hai un linguaggio di programmazione, dì X. Come eseguirai i suoi programmi? È possibile interpretare il codice sorgente o compilarlo in codice macchina oppure compilarlo in un codice intermedio. Ora hai un altro linguaggio di programmazione, Y, e vuoi implementarlo usando X. Se entrambe le implementazioni sono interprete, avrai l'interprete di Y in esecuzione sull'interprete di X, e questo sarà molto lento.
18446744073709551615

11

Perché le VM devono essere "macchine stack" o "macchine registratrici" ecc.?

Loro non. Se hai bisogno di una macchina virtuale, potrebbe essere qualsiasi cosa.

Le macchine virtuali esistenti sono apparse come soluzioni a situazioni come: Mi è venuta in mente un'idea davvero geniale, ho inventato un nuovo linguaggio di programmazione! Ma devo generare codice. (Che compito noioso!) Ma non voglio generare codice i8086 perché è brutto e non voglio generare codice 68k perché tutti gli altri usano Intel. C'è anche VAX, ma non ho alcun VAX, né un computer né un libro VAX. Pertanto genererò codice per alcuni processori che non esistono fisicamente e implementerò quel processore nel software. Le specifiche di quella VM faranno un capitolo della mia tesi. In teoria, sarà possibile compilarlo in un codice nativo di qualsiasi processore, ma non sarò io.

D'altra parte, la notazione come "2 + 3" probabilmente non verrà utilizzata dalle macchine virtuali in un futuro prevedibile perché implica fare molta trasformazione prima che qualcosa possa essere eseguito.


Grazie per aver risposto. Quindi quello che ho raccolto dalla tua risposta è che la motivazione per progettare una VM che emula CPU fisiche è perché semplifica l'implementazione successiva di compilatori che vengono compilati in base al vero codice macchina. Ma a parte questo, ci sono dei vantaggi nella progettazione di una VM in termini di "macchina stack" o "macchina di registrazione" ecc.?
Aviv Cohn,

1
I registri richiedono algoritmi di allocazione dei registri, che richiedono sia teoria che debug. Una macchina stack (in particolare uno operando zero) può semplicemente posizionare i dati nello stack. OTOH, l'hardware di solito implementa un numero limitato di registri anziché uno stack di dimensioni variabili. Quindi gli stack sono più facili per il software, i registri sono più facili per l'hardware e probabilmente quindi un po 'più veloci.
18446744073709551615

-2

Per rispondere alla domanda effettiva che è stata posta. Il termine "MACCHINA virtuale" significa che TUTTO il software / hardware viene simulato / emulato. Se si utilizza il software / hardware sottostante per eseguire le istruzioni, non si dispone di una macchina virtuale, si dispone di un compilatore / interprete.


è solo una tua opinione o puoi sostenerla in qualche modo?
moscerino il

@Kyrelel non è vero. L'hardware "TUTTO" viene emulato nella VM "di sistema" o "completa". Non tutte le VM sono piene. Ad esempio, il livello VM BSD è chiamato "macchina virtuale", nonostante l'hardware non sia emulato lì.
Netch

Non penso che la domanda riguardi necessariamente la terminologia, ma piuttosto perché le macchine virtuali implementano funzionalità che apparentemente sono già gestite dall'hardware reale
Ryan,
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.