Pourquoi les machines virtuelles doivent-elles être des «machines empilées» ou des «machines enregistreuses», etc.?

48

(C'est une question extrêmement novice).

J'ai étudié un peu les machines virtuelles.

Il s'avère que beaucoup d'entre eux sont conçus de manière très similaire aux ordinateurs physiques ou théoriques.

J'ai lu que la machine virtuelle Java, par exemple, est une «machine à pile». Ce que cela signifie (et corrigez-moi si je me trompe), c'est qu'il stocke toute sa "mémoire temporaire" sur une pile et effectue des opérations sur cette pile pour tous ses opcodes.

Par exemple, le code source 2 + 3sera traduit en bytecode similaire à:

push 2
push 3
add

Ma question est la suivante:

Les machines virtuelles sont probablement écrites en C / C ++, etc. Si tel est le cas, pourquoi la machine virtuelle Java n'exécute-t-elle pas le code C suivant: 2 + 3..? Je veux dire, pourquoi a-t-il besoin d'une pile ou des "registres" d'autres ordinateurs virtuels - comme dans un ordinateur physique?

Le processeur physique sous-jacent prend en charge tout cela. Pourquoi les auteurs de machines virtuelles n'exécutent-ils pas simplement le code-octet interprété avec des instructions "habituelles" dans la langue avec laquelle la machine virtuelle est programmée?

Pourquoi les machines virtuelles doivent-elles émuler du matériel alors que le matériel actuel le fait déjà pour nous?

Encore une fois, très newbie-ish questions. Merci de votre aide

Aviv Cohn
la source
5
Avez-vous envisagé les machines non virtuelles sur lesquelles sont basées?
5
@MichaelT Vous voulez dire des machines physiques?
Aviv Cohn
Bien sûr, la plupart des machines virtuelles Javascript ne sont pas des machines empilées ou des machines à enregistrer - V8 / IonMonkey / Chakra / etc. sont des machines virtuelles qui implémentent Javascript. Une "VM" est juste un interpréteur ou un compilateur JIT qui peut implémenter n'importe quel langage que le concepteur souhaite.
Billy ONeal
@BillyONeal Donc, par exemple, si j'écris une machine virtuelle pour une langue et que je l'écris en C: la machine virtuelle analyse la ligne de bytcode 'print "hi"' et l'exécute printf("hi");: s'agit-il d'une machine virtuelle? Il n'a pas de "pile" ou "registres" et rien.
Aviv Cohn
@Prog: Oui, c'est vrai.
Billy ONeal

Réponses:

51

Une machine, virtuelle ou non, a besoin d’un modèle de calcul décrivant comment le calcul est effectué. Par définition, dès qu'il calcule, il implémente un modèle de calcul. La question est alors: quel modèle devrions-nous choisir pour notre machine virtuelle? Les machines physiques sont limitées par ce que l’on peut faire de manière efficace dans le matériel. Mais, comme vous le constatez, les machines virtuelles ne sont pas soumises à de telles contraintes. Elles sont définies dans un logiciel utilisant des langages de haut niveau arbitrairement.

En fait, il existe des machines virtuelles de haut niveau, telles que vous les décrivez. Ils s'appellent les langages de programmation . Le standard C, par exemple, consacre l’essentiel de ses pages à la définition d’un modèle pour la "machine abstraite en C", qui décrit le comportement des programmes C et, par extension, la manière dont un compilateur C conforme (ou un interpréteur) se conforme. Devrait se comporter.

Bien sûr, nous n’appelons généralement pas cela une machine virtuelle. Une machine virtuelle signifie généralement quelque chose de niveau inférieur, plus proche du matériel, non destiné à être programmé directement, conçu pour être exécuté efficacement. Ce biais de sélection signifie que quelque chose qui accepte du code composable de haut niveau (comme ce que vous décrivez) ne serait pas considéré comme une machine virtuelle, car il exécute du code de haut niveau.

Mais pour aller droit au but, voici quelques raisons de créer une machine virtuelle (comme dans quelque chose ciblé par un compilateur de code-octet) basée sur des registres ou similaire. Les machines à empiler et à enregistrer sont extrêmement simples. Il y a une séquence d'instructions, un certain état et une sémantique pour chaque instruction (une fonction State -> State). Pas de réduction d'arbres complexes, pas de priorité des opérateurs. Son analyse, son analyse et son exécution sont très simples, car il s'agit d'un langage minimal (le sucre syntaxique est compilé) et conçu pour être lu par une machine plutôt que par un humain.

En revanche, analyser même les langages de type C les plus simples est assez difficile et son exécution nécessite des analyses non locales telles que la vérification et la propagation de types, la résolution des surcharges, la maintenance d'une table de symboles, la résolution des identificateurs de chaîne , la transformation de texte linéaire en AST , etc. Il s’appuie sur des concepts naturels pour l’homme, mais doit être minutieusement mis au point par des machines.

Le bytecode de la machine virtuelle Java, par exemple, est émis par javac. Il n’a pratiquement jamais besoin d’être lu ou écrit par des humains, il est donc naturel de l’orienter vers la consommation par des machines. Si vous optimized pour l' homme, la machine virtuelle Java serait juste à chaque démarrage lire le code, l' analyser, analyser est, puis le convertir en une représentation intermédiaire ressemblant à un tel modèle simplifié de la machine de toute façon . Pourrait aussi bien couper l'homme du milieu.


la source
Donc, ce que vous dites, c'est que tout compiler en instructions sur la pile (c'est-à System.out.println("hi");- dire compiler en une instruction sur une pile, int a = 7est compilé en une instruction sur la pile, etc.) rend l'exécution du programme simple et plus efficace?
Aviv Cohn
2
@ Prog En gros, oui. Mais pas seulement l'exécution, mais aussi l'analyse. Tout ce qui est fait par programme.
Pourtant, je ne comprends pas pourquoi 2 + 3est compilé pour push 2 push 3 add. L' addétape à l'extrémité est exécutée par la machine virtuelle Java de toute façon en exécutant le code C 2 + 3. Les programmeurs de la machine virtuelle Java n'ont aucun autre moyen de le faire. Pourquoi ne pas le compiler 2 + 3et laisser la machine virtuelle exécuter simplement le code C 2 + 3(en supposant qu’il soit écrit en C) immédiatement?
Aviv Cohn
@Prog L'auteur de la machine virtuelle Java ne peut pas simplement écrire 2 + 3dans le code source de la machine virtuelle, car celle-ci doit fonctionner avec n'importe quel programme effectuant des opérations dans n'importe quel ordre. Construire du code source en C et passer à une implémentation C ne fait que poser le même problème à la mise en œuvre C (et cela ne peut pas être fait facilement, encore moins efficacement). Une structure de données doit être décrite pour que le programme puisse être interprété et compilé au format JIT. Le "code source lisible par l'homme" est un choix épouvantable de structure de données pour les raisons exposées ci-dessus.
7
@Prog Vous semblez trop concentré sur le cas spécifique des 2 + 3. Et vous a + b? Ensuite, les valeurs à ajouter ne proviennent pas i.argument{1,2}, elles sont chargées à partir de variables locales. Qu'en est- il frobnicate(x[i]) + (Foo.bar() * 2)? En utilisant cette conception, il n'y a qu'une seule addopération (pour int) et cela fonctionne indépendamment de la façon dont les addend ont été calculés. De plus, une instruction qui ajoute uniquement des littéraux entiers serait inutile: son résultat pourrait tout aussi bien être pré-calculé (c'est-à-dire add(2,3)qu'il devrait l'être push(5)).
20

Cette réponse se concentre sur la machine virtuelle, mais en réalité elle s’applique à n’importe quelle machine virtuelle.

Pourquoi les machines virtuelles doivent-elles émuler du matériel alors que le matériel actuel le fait déjà pour nous?

Ce n’est pas le cas, mais cela rend la machine virtuelle beaucoup plus simple et portable: une machine virtuelle qui émule le matériel peut utiliser le même modèle de calcul que n’importe quel processeur matériel.

La JVM, en particulier, a été conçue pour la portabilité. Elle a même été conçue pour pouvoir être mise en œuvre matériellement (il est peut-être difficile de le croire aujourd’hui, mais l’ origine de Java était dans le monde des systèmes embarqués, en particulier les contrôleurs pour la télévision interactive. ).

Si vous avez un objectif de ce type, il est souhaitable que la machine virtuelle fonctionne aussi près que possible d'une machine physique, car la conversion en code machine réel devient plus facile et donc plus rapide. Une fois que vous avez les codes d'opération de la machine virtuelle, en théorie, tout ce que vous avez à faire est de les convertir en codes d'opération de la CPU sur laquelle le programme est exécuté. En pratique, ce n'est pas si simple.

Je veux dire, pourquoi a-t-il besoin d'une pile ou des "registres" d'autres ordinateurs virtuels - comme dans un ordinateur physique?

L'utilisation d'un modèle de machine virtuelle basé sur une pile présente l'avantage de pouvoir être facilement transféré à la fois sur des machines à registre et sur des machines à pile, alors que l'inverse n'est pas nécessairement vrai. Une machine virtuelle basée sur des registres devrait émettre des hypothèses sur le nombre de registres, leur taille, etc. Avec une machine à pile, aucune hypothèse de ce type n'est nécessaire.

Le processeur physique sous-jacent prend en charge tout cela. Pourquoi les auteurs de machines virtuelles n'exécutent-ils pas simplement le code-octet interprété avec des instructions "habituelles" dans la langue avec laquelle la machine virtuelle est programmée?

Eh bien, c’est ce que font ces machines virtuelles: elles interprètent le bytecode. Même la machine virtuelle le fait effectivement, du moins avant que JIT (juste à temps) n’intervienne: elle interprète les codes d’octet et exécute les instructions dans le langage dans lequel elle a été écrite (généralement C ou C ++, mais il en existe même un écrit). en JavaScript, Doppio ). Notez cependant que même ces déclarations ont été traduites en code machine par un compilateur et ont en fait une apparence très similaire à celle du compilateur Java, à savoir qu’elles utilisent des registres et la pile pour effectuer leur travail. Notez que l'utilisation des langages "interprétés" vs "compilés" devient un peu floue à ce stade.

miraculixx
la source
Bien entendu, tout ce qui peut être implémenté dans un logiciel peut être implémenté dans un matériel. En outre, la machine virtuelle Java est actuellement un compilateur JIT - elle n'exécute pas les instructions dans le langage dans lequel la machine virtuelle a été écrite. Si cela se produisait, Java serait terriblement performant et ne serait plus une plateforme aussi viable qu'aujourd'hui. . (Enfer, la plupart des implémentations Javascript seraient plus rapides)
Billy ONeal
2
@BillyONeal "Plutôt que de compiler, méthode par méthode, la machine virtuelle Java HotSpot exécute immédiatement le programme à l'aide d'un interpréteur et analyse le code lors de son exécution afin de détecter les points chauds critiques du programme. optimiseur global de code natif sur les points chauds ", cité par oracle.com/technetwork/java/whitepaper-135217.html#2 , section" Détection de points chauds "
miraculixx le
Oui. "Optimiseur de code natif" == compilation JIT. Il y a une phase interpréteur pour le code qui ne semble pas être "chaude" pour éviter que JIT utilise des choses rarement utilisées. Mais cela ne signifie pas qu'aucune JIT n'est faite du tout.
Billy ONeal
Merci de répondre. D'après votre réponse, ce que j'ai compris de votre réponse est que les raisons d'émuler du matériel dans la machine virtuelle (c'est-à-dire avec "piles" ou "registres", etc.) sont dues au fait qu'il est facile de compiler ultérieurement le bytecode ou le code source en code machine réel. CPU physique. Cependant, mis à part cela, y a-t-il un avantage à émuler du matériel dans une machine virtuelle? Je ne comprends toujours pas pourquoi quelqu'un qui conçoit une machine virtuelle pense en termes de «machine à pile» ou «machine à registre», etc., alors qu'il s'agit en réalité de logiciel. Est-ce que je manque quelque chose?
Aviv Cohn
@ Prog. Ok, vous avez un langage de programmation, disons X. Comment allez-vous exécuter ses programmes? Vous pouvez soit interpréter la source, soit la compiler en code machine, ou la compiler en un code intermédiaire. Vous avez maintenant un autre langage de programmation, Y, et vous souhaitez l'implémenter à l'aide de X. Si les deux implémentations sont un interpréteur, l'interprète de Y s'exécutera sur l'interpréteur de X, ce qui sera très lent.
18446744073709551615
11

Pourquoi les machines virtuelles doivent-elles être des «machines empilées» ou des «machines enregistreuses», etc.?

Ils ne. Si vous avez besoin d'une machine virtuelle, cela peut être n'importe quoi.

Les machines virtuelles existantes sont apparues comme des solutions aux situations suivantes: Une idée vraiment brillante m'est venue à la tête, j'ai inventé un nouveau langage de programmation! Mais je dois générer du code. (Quelle tâche ennuyeuse!) Mais je ne veux pas générer de code i8086 parce que c'est moche, et je ne veux pas générer de code 68k car tout le monde utilise Intel. Il y a aussi VAX, mais je n'ai ni VAX, ni ordinateur, ni livre VAX. Par conséquent, je vais générer du code pour un processeur qui n'existe pas physiquement et l'implémenter dans un logiciel. La spécification de cette VM fera un chapitre de ma thèse. En théorie, il sera possible de le compiler en code natif de tout processeur, mais ce ne sera pas moi.

D'autre part, la notation telle que "2 + 3" ne sera probablement pas utilisée par les ordinateurs virtuels dans un avenir prévisible, car elle implique de nombreuses transformations avant que quelque chose puisse être exécuté.

18446744073709551615
la source
Merci de répondre. Donc, ce que j’ai compris de votre réponse, c’est que la motivation pour concevoir une machine virtuelle qui émule des processeurs physiques tient au fait qu’il est facile d’implémenter ultérieurement des compilateurs compilant au code machine réel. Mais à part cela, y a-t-il des avantages à concevoir une VM en termes de «machine à pile» ou de «machine à registre», etc.?
Aviv Cohn
1
Les registres nécessitent des algorithmes d’allocation de registres, qui nécessitent à la fois de la théorie et du débogage. Une machine à pile (en particulier une machine à zéro opérande) peut simplement placer les données sur la pile. OTOH, le matériel implémente généralement un nombre limité de registres plutôt qu'une pile de taille variable. Les piles sont donc plus faciles pour les logiciels, les registres plus faciles pour le matériel et probablement un peu plus rapidement.
18446744073709551615
-2

Pour répondre à la question qui a été posée. Le terme "MACHINE virtuelle" signifie que TOUS les logiciels / matériels sont simulés / émulés. Si vous utilisez le logiciel / matériel sous-jacent pour exécuter les instructions, vous n'avez pas de machine virtuelle, vous avez un compilateur / interprète.

Kyrelel
la source
S'agit-il simplement de votre opinion ou pouvez-vous l'assurer d'une manière ou d'une autre?
moucher
@ Kirelel c'est faux. Le matériel "ALL" est émulé dans la VM "système" ou "complète". Toutes les machines virtuelles ne sont pas pleines. Par exemple, la couche VM BSD est nommée "machine virtuelle", bien que le matériel n'y soit pas émulé.
Netch
Je ne pense pas que la question concerne nécessairement la terminologie, mais plutôt pourquoi les machines virtuelles implémentent des fonctionnalités qui sont apparemment déjà gérées par le matériel actuel
Ryan