Por que as VMs precisam ser "máquinas de empilhar" ou "registrar máquinas" etc.?

48

(Esta é uma pergunta extremamente nova para os novatos).

Venho estudando um pouco sobre máquinas virtuais.

Acontece que muitos deles são projetados de maneira muito semelhante a computadores físicos ou teóricos.

Eu li que a JVM, por exemplo, é uma 'máquina de empilhar'. O que isso significa (e me corrija se eu estiver errado) é que ele armazena toda a sua 'memória temporária' em uma pilha e faz operações nessa pilha para todos os seus códigos de operação.

Por exemplo, o código-fonte 2 + 3será traduzido para bytecode semelhante a:

push 2
push 3
add

Minha pergunta é esta:

As JVMs provavelmente são escritas usando C / C ++ e tal. Se sim, por que a JVM não executa o seguinte código C: 2 + 3..? Quero dizer, por que ele precisa de uma pilha ou em outros 'registros' de VMs - como em um computador físico?

A CPU física subjacente cuida de tudo isso. Por que os gravadores de VM simplesmente não executam o bytecode interpretado com instruções 'usuais' no idioma em que a VM está programada?

Por que as VMs precisam emular o hardware, quando o hardware real já faz isso por nós?

Novamente, perguntas muito novas. Obrigado pela ajuda

Aviv Cohn
fonte
5
Você já considerou em que máquinas não virtuais são baseadas?
5
@MichaelT Você quer dizer máquinas físicas?
Aviv Cohn
Obviamente, a maioria das VMs Javascript não são máquinas de empilhamento ou registradoras - V8 / IonMonkey / Chakra / etc. são VMs que implementam Javascript. Uma "VM" é apenas um intérprete ou compilador JIT que pode implementar qualquer idioma que o designer desejar.
Billy ONeal
@BillyONeal Então, por exemplo, se estou escrevendo uma VM para algum idioma e em C: a VM analisa a linha de bytcode 'print "hi"' e executa printf("hi");: isso é considerado uma VM? Não possui 'pilha' ou 'registradores' e nem nada.
Aviv Cohn
@ Prog: Sim, isso está correto.
Billy ONeal

Respostas:

51

Uma máquina, virtual ou não, precisa de um modelo de computação que descreva como a computação é realizada nela. Por definição, assim que calcula, implementa algum modelo de computação. A questão então é: qual modelo devemos escolher para nossa VM? Máquinas físicas são limitadas pelo que pode ser feito de maneira eficaz e eficiente em hardware. Mas, como você observa, as máquinas virtuais não têm essas restrições, elas são definidas no software usando linguagens arbitrariamente de alto nível.

De fato, existem máquinas virtuais de alto nível, como você descreve. Eles são chamados de linguagens de programação . O padrão C, por exemplo, dedica a maior parte de suas páginas à definição de um modelo para a chamada "máquina abstrata C", que descreve como o programa C se comporta e, por extensão (como regra geral), como um compilador (ou intérprete) compatível com C Deve se comportar.

Obviamente, geralmente não chamamos isso de máquina virtual. Geralmente, uma VM significa algo de nível inferior, mais próximo ao hardware, que não se destina a ser diretamente programado, projetado para ser executado com eficiência. Esse viés de seleção significa que algo que aceita código composível de alto nível (como o que você descreve) não seria considerado uma VM porque executa código de alto nível.

Mas, para ir direto ao ponto, aqui estão alguns motivos para criar uma VM (como em algo direcionado por um compilador de bytecode) com base em registro ou algo semelhante. Máquinas de empilhar e registrar são extremamente simples. Há uma sequência de instruções, algum estado e semântica para cada instrução (uma função Estado -> Estado). Sem reduções complexas de árvores, sem precedência do operador. Analisar, analisar e executar é muito simples, porque é uma linguagem mínima (o açúcar sintático é compilado) e projetado para ser lido por máquina em vez de humano.

Por outro lado, analisar até mesmo as linguagens C mais simples é bastante difícil e executá-lo exige análises não locais, como verificação e propagação de tipos, resolução de sobrecargas, manutenção de uma tabela de símbolos, resolução de identificadores de sequência , transformando texto linear em um AST baseado em precedência , e assim por diante. Ele se baseia em conceitos que são naturais para os seres humanos, mas precisam ser minuciosamente projetados de maneira reversa pelas máquinas.

O bytecode da JVM, por exemplo, é emitido por javac. Praticamente nunca precisa ser lido ou escrito por seres humanos, por isso é natural orientá-lo para o consumo pelas máquinas. Se você é otimizado para os seres humanos, a JVM seria apenas em cada partida ler o código, analisá-lo, analisar é, em seguida, convertê-lo em uma representação intermediária semelhante a uma simplificada tal modelo de máquina de qualquer maneira . Poderia muito bem cortar o intermediário.


fonte
Então, o que você está dizendo é que compilar tudo com instruções na pilha (ou seja, System.out.println("hi");é compilado com alguma instrução em uma pilha, int a = 7compilado com uma instrução na pilha, etc.) torna a execução do programa simples e mais eficiente?
Aviv Cohn
2
@ Pro Basicamente, sim. Mas não apenas execução, também análise. Tudo o que é feito programaticamente.
Ainda assim, não entendo por que 2 + 3é compilado push 2 push 3 add. O addpasso no final é executada pela JVM de qualquer maneira, executando o código C 2 + 3. Não há outra maneira de os programadores da JVM fazerem isso. Por que não compilá-lo 2 + 3e fazer com que a JVM execute o código C 2 + 3(supondo que esteja escrito em C) imediatamente?
Aviv Cohn
@Prog O autor da JVM não pode simplesmente escrever 2 + 3no código-fonte da JVM porque a JVM precisa trabalhar com qualquer programa que execute operações em qualquer ordem. Criar código-fonte C e adiar para uma implementação C apenas empurra o mesmo problema para a implementação C (e não pode ser feito facilmente, muito menos com eficiência). Deve haver alguma estrutura de dados que descreva o programa, para que possa ser interpretado e compilado pelo JIT, e o "código-fonte legível por humanos" é uma péssima escolha da estrutura de dados pelas razões descritas acima.
7
@Prog Você parece muito focado no caso específico de 2 + 3. Que tal a + b? Os valores a serem adicionados não são originários i.argument{1,2}, eles são carregados a partir de variáveis ​​locais. Que tal frobnicate(x[i]) + (Foo.bar() * 2)? Usando esse design, existe apenas uma addoperação (para int) e funciona independentemente de como os addends foram computados. Além disso, uma instrução que adiciona apenas literais inteiros seria inútil: seu resultado também poderia ser pré-calculado (ou seja, em vez disso add(2,3), deveria ser push(5)).
20

Esta resposta se concentra na JVM, mas na verdade se aplica a qualquer VM.

Por que as VMs precisam emular o hardware, quando o hardware real já faz isso por nós?

Não, mas torna a VM muito mais simples e portátil: uma VM que emula hardware pode usar o mesmo modelo computacional que qualquer CPU de hardware.

A JVM, em particular, foi construída com a portabilidade em mente; na verdade, foi construída para ser implementada em hardware (pode ser difícil acreditar hoje, mas a origem do Java estava no mundo incorporado - especificamente, controladores para televisão interativa )

Se você tiver um objetivo como esse, é desejável que a VM opere o mais próximo possível de uma máquina física, pois a conversão para o código de máquina real se torna mais fácil e, portanto, mais rápida. Depois de ter os códigos de operação da VM, em teoria, tudo o que você precisa fazer é converter os códigos de operação da CPU na qual o programa realmente é executado. Na prática, não é exatamente assim tão simples.

Quero dizer, por que ele precisa de uma pilha ou em outros 'registros' de VMs - como em um computador físico?

O uso de um modelo de máquina virtual baseado em pilha tem a vantagem de poder ser facilmente transferido para máquinas de registro e pilha, enquanto o oposto não é necessariamente verdadeiro. Uma VM baseada em registro precisaria fazer suposições sobre o número de registros, o tamanho dos registros etc. Com uma máquina de empilhar, essas suposições não são necessárias.

A CPU física subjacente cuida de tudo isso. Por que os gravadores de VM simplesmente não executam o bytecode interpretado com instruções 'usuais' no idioma em que a VM está programada?

Bem, é isso que essas VMs fazem, elas interpretam o bytecode. Até a JVM realmente faz isso, pelo menos antes do JIT (just-in-time) entrar em ação: interpreta os códigos de byte e executa as instruções no idioma em que a JVM foi escrita (normalmente C ou C ++, mas há até uma escrita) em JavaScript, Doppio ). Observe, no entanto, que mesmo essas instruções foram traduzidas para código de máquina por um compilador e, na verdade, parecem muito semelhantes ao que o compilador Java produz - ou seja, eles usam registradores e a pilha para executar seu trabalho. Observe que o uso de linguagens "interpretadas" vs "compiladas" se torna um pouco embaçado nesse momento.

miraculixx
fonte
Obviamente, qualquer coisa que possa ser implementada em software pode ser implementada em hardware. Além disso, a JVM atualmente (ponto de acesso) é um compilador JIT - não executa as instruções no idioma em que a JVM foi escrita. . (Inferno, a maioria das implementações de JavaScript seria mais rápido)
Billy ONeal
2
@BillyONeal "Em vez de compilar método por método, bem a tempo, a Java HotSpot VM executa imediatamente o programa usando um intérprete e analisa o código executado para detectar os pontos críticos críticos do programa. Depois, concentra a atenção de um otimizador global em código nativo sobre os pontos quentes "citou. oracle.com/technetwork/java/whitepaper-135217.html#2 , seção 'Detecção de Hot spot'
miraculixx
Sim. "Otimizador de código nativo" == Compilação JIT. Há uma fase de intérprete para o código que não parece estar "quente" para evitar o JIT de coisas raramente usadas. Mas isso não significa que nenhum JIT seja feito.
Billy ONeal
Obrigado por responder. O que eu deduzi da sua resposta é que os motivos para emular o hardware na VM (também conhecido como 'stacks' ou 'registradores' etc.) é porque facilita a compilação posterior do bytecode ou do código-fonte no código de máquina real de um CPU física. No entanto, além disso - há algo a ganhar com a emulação de hardware em uma VM? Ainda não entendo por que alguém projetando uma VM pensaria em termos de 'máquina de empilhar' ou 'máquina de registro' etc. etc., quando na verdade estamos falando de software. Estou esquecendo de algo?
Aviv Cohn
@ Prog Ok, você tem uma linguagem de programação, diga X. Como você executará seus programas? Você pode interpretar a fonte ou compilá-la no código da máquina ou compilá-la em algum código intermediário. Agora você tem outra linguagem de programação, Y, e deseja implementá-la usando X. Se ambas as implementações forem intérpretes, você terá o intérprete de Y sendo executado no intérprete de X, e isso será muito lento.
18446744073709551615
11

Por que as VMs precisam ser "máquinas de empilhar" ou "registrar máquinas" etc.?

Eles não. Se você precisar de uma máquina virtual, pode ser qualquer coisa.

As máquinas virtuais existentes apareceram como soluções para situações como: Uma ideia realmente brilhante me veio à cabeça: eu inventei uma nova linguagem de programação! Mas eu tenho que gerar código. (Que tarefa chata!) Mas eu não quero gerar código i8086 porque é feio e não quero gerar código 68k porque todo mundo está usando a Intel. Também existe o VAX, mas eu não tenho nenhum VAX, nem um computador nem um livro do VAX. Portanto, gerarei código para algum processador que não existe fisicamente e implementarei esse processador em software. As especificações dessa VM farão um capítulo na minha tese. Em teoria, será possível compilá-lo no código nativo de qualquer processador, mas isso não será eu.

Por outro lado, a notação como "2 + 3" provavelmente não será usada pelas VMs em um futuro próximo, pois implica muita transformação antes que algo possa ser executado.

18446744073709551615
fonte
Obrigado por responder. Então, o que eu deduzi da sua resposta é que a motivação para projetar uma VM que emula CPUs físicas é porque facilita a implementação posterior de compiladores que são compilados com o código de máquina real. Mas além disso - existem vantagens em projetar uma VM em termos de uma 'máquina de empilhamento' ou 'máquina de registro' etc.?
Aviv Cohn
1
Os registros requerem algoritmos de alocação de registros, que precisam de teoria e depuração. Uma máquina de pilha (especialmente uma de operando zero) pode apenas colocar os dados na pilha. OTOH, o hardware geralmente implementa uma quantidade limitada de registros, em vez de uma pilha de tamanho variável. Portanto, as pilhas são mais fáceis para o software, os registros são mais fáceis para o hardware e, provavelmente, um pouco mais rápido.
18446744073709551615
-2

Para responder à pergunta real que foi feita. O termo "MÁQUINA virtual" significa que TODOS os softwares / hardwares são simulados / emulados. Se você usa o software / hardware subjacente para executar as instruções, não possui uma VM, possui um compilador / intérprete.

Kyrelel
fonte
esta é apenas sua opinião ou você pode apoiá-la de alguma forma?
gnat 30/10
@Kyrelel isso é falso. O hardware "ALL" é emulado na VM "system" ou "full". Nem todas as VMs estão cheias. Por exemplo, a camada VM do BSD é denominada "máquina virtual", apesar de o hardware não ser emulado lá.
Netch 30/10
Eu não acho que a questão é necessariamente sobre a terminologia, mas sim porque as máquinas virtuais implementar a funcionalidade que é aparentemente já tratado pelo hardware real
Ryan