(To jest pytanie bardzo początkujące).
Studiowałem trochę na temat maszyn wirtualnych.
Okazuje się, że wiele z nich zaprojektowano bardzo podobnie do komputerów fizycznych lub teoretycznych.
Przeczytałem, że na przykład JVM jest „maszyną stosową”. Oznacza to (i popraw mnie, jeśli się mylę), że przechowuje całą swoją „pamięć tymczasową” na stosie i wykonuje operacje na tym stosie dla wszystkich swoich kodów operacyjnych.
Na przykład kod źródłowy 2 + 3
zostanie przetłumaczony na kod bajtowy podobny do:
push 2
push 3
add
Moje pytanie brzmi:
JVM są prawdopodobnie napisane przy użyciu C / C ++ i tym podobnych. Jeśli tak, dlaczego JVM nie wykonuje następującego kodu C: 2 + 3
..? To znaczy, dlaczego potrzebuje stosu lub innych „rejestrów” maszyn wirtualnych - jak na fizycznym komputerze?
Zajmuje się tym fizyczny procesor. Dlaczego autorzy maszyn wirtualnych nie wykonują po prostu interpretowanego kodu bajtowego z „zwykłymi” instrukcjami w języku, w którym jest zaprogramowana maszyna wirtualna?
Dlaczego maszyny wirtualne muszą emulować sprzęt, skoro sam sprzęt już to dla nas robi?
Znów bardzo nowe pytania. Dzięki za pomoc
źródło
printf("hi");
: czy jest to uważane za maszynę wirtualną? Nie ma „stosu”, „rejestrów” ani niczego.Odpowiedzi:
Maszyna, wirtualna czy nie, potrzebuje modelu obliczeń, który opisuje sposób wykonywania na nim obliczeń. Z definicji, jak tylko wykonuje obliczenia, implementuje pewien model obliczeń. Pytanie brzmi zatem: jaki model wybrać dla naszej maszyny wirtualnej? Maszyny fizyczne są ograniczone przez to, co można skutecznie i wydajnie wykonać w sprzęcie. Ale, jak zauważasz, maszyny wirtualne nie mają takich ograniczeń, są definiowane w oprogramowaniu przy użyciu arbitralnie języków wysokiego poziomu.
W rzeczywistości istnieją maszyny wirtualne, które są opisane na wysokim poziomie. Nazywa się je językami programowania . Na przykład standard C poświęca większość swoich stron na zdefiniowanie modelu dla tak zwanej „maszyny abstrakcyjnej C”, która opisuje, jak zachowują się programy C, a przez rozszerzenie (reguła as-if), w jaki sposób zgodny kompilator C (lub interpreter) powinien się zachowywać.
Oczywiście zwykle nie nazywamy tego maszyną wirtualną. VM zwykle oznacza coś niższego poziomu, bliżej sprzętu, nieprzeznaczonego do bezpośredniego programowania, zaprojektowanego do wydajnego wykonywania. To odchylenie wyboru oznacza, że coś, co akceptuje kod kompozycyjny wysokiego poziomu (jak to, co opisujesz) nie będzie uważane za maszynę wirtualną, ponieważ wykonuje kod wysokiego poziomu.
Ale, aby przejść do sedna, oto kilka powodów, dla których maszynę wirtualną (jak w, czymś docelowym kompilatora bajtów) opiera się na rejestrze lub tym podobne. Urządzenia do układania i rejestrowania są niezwykle proste. Dla każdej instrukcji istnieje sekwencja instrukcji, niektóre stany i semantyka (funkcja Stan -> Stan). Bez złożonych redukcji drzew, bez pierwszeństwa operatora. Analiza, analiza i wykonywanie jest bardzo proste, ponieważ jest to minimalny język (cukier syntaktyczny jest kompilowany) i zaprojektowany do odczytu maszynowego, a nie ludzkiego.
Natomiast parsowanie nawet najprostszych języków podobnych do C jest dość trudne, a jego wykonanie wymaga nielokalnych analiz, takich jak sprawdzanie i propagowanie typów, rozwiązywanie przeciążeń, utrzymywanie tabeli symboli, rozwiązywanie identyfikatorów ciągów , przekształcanie tekstu liniowego w AST sterowaną pierwszeństwem , i tak dalej. Opiera się na koncepcjach, które są naturalne dla ludzi, ale muszą zostać starannie opracowane przez maszyny.
Na przykład kod bajtowy JVM jest emitowany przez
javac
. Praktycznie nigdy nie musi być odczytywany ani zapisywany przez ludzi, więc naturalne jest, aby dostosować go do zużycia przez maszyny. Jeśli zoptymalizowany go dla ludzi, JVM byłoby po prostu na każdym starcie odczytać kodu, analizować je, analizować to, a następnie przekształcić go w pośrednim reprezentacji przypominający taki uproszczony model maszyny anyway . Równie dobrze może odciąć środkowego mężczyznę.źródło
System.out.println("hi");
Jest kompilowana do niektórych instrukcji na stosie,int a = 7
jest kompilowana do instrukcji na stosie itp.) Sprawia, że wykonywanie programu jest proste i bardziej wydajne?2 + 3
jest kompilowanypush 2 push 3 add
.add
Krok na końcu jest wykonywany przez JVM i tak przez uruchomienie kodu C2 + 3
. Programiści JVM nie mogą tego zrobić. Dlaczego nie skompilować go2 + 3
i pozwolić JVM po prostu wykonać kod C2 + 3
(zakładając, że jest napisany w C) od razu?2 + 3
w kodzie źródłowym JVM, ponieważ JVM musi współpracować z dowolnym programem wykonującym dowolne operacje w dowolnej kolejności. Budowanie kodu źródłowego C i odraczanie do implementacji C po prostu popycha ten sam problem do implementacji C (i nie można tego łatwo zrobić, a tym bardziej skutecznie). Musi istnieć jakaś struktura danych opisująca program, aby można go było interpretować i kompilować JIT, a „kod źródłowy czytelny dla człowieka” to okropny wybór struktury danych z powodów przedstawionych powyżej.a + b
? Następnie wartości, które należy dodać, nie pochodząi.argument{1,2}
, są one ładowane ze zmiennych lokalnych. Cofrobnicate(x[i]) + (Foo.bar() * 2)
? Korzystając z tego projektu, istnieje tylko jednaadd
operacja (dlaint
) i działa ona niezależnie od sposobu obliczania dodatków. Ponadto instrukcja, która dodaje tylko literały całkowite, byłaby bezcelowa: jej wynik mógłby równie dobrze zostać wstępnie obliczony (tzn. Zamiastadd(2,3)
tego powinien byćpush(5)
).Ta odpowiedź dotyczy JVM, ale w rzeczywistości dotyczy dowolnej maszyny wirtualnej.
Nie robią tego, ale sprawia, że maszyna wirtualna jest znacznie prostsza i przenośna: maszyna wirtualna, która emuluje sprzęt, może korzystać z tego samego modelu obliczeniowego, co dowolny sprzętowy procesor.
W szczególności JVM został zbudowany z myślą o przenośności, w rzeczywistości został zbudowany, aby mógł być nawet zaimplementowany w sprzęcie (dziś trudno w to uwierzyć, ale Java powstała w świecie osadzonym - w szczególności w kontrolerach telewizji interaktywnej ).
Jeśli masz taki cel, pożądane jest, aby maszyna wirtualna działała jak najbliżej fizycznej maszyny, ponieważ tłumaczenie na rzeczywisty kod maszynowy staje się łatwiejsze, a tym samym szybsze. Gdy masz już kody maszyn wirtualnych, teoretycznie wszystko, co musisz zrobić, to przetłumaczyć na kody procesora, na którym program faktycznie działa. W praktyce nie jest to takie proste.
Zastosowanie modelu maszyny wirtualnej opartej na stosie ma tę zaletę, że można ją łatwo przenieść zarówno na maszyny rejestrujące, jak i na stos, podczas gdy odwrotnie nie jest to prawda. Maszyna wirtualna oparta na rejestrach musiałaby przyjąć założenia dotyczące liczby rejestrów, wielkości rejestrów itp. W przypadku maszyny stosowej takie założenia nie są konieczne.
Tak właśnie robią takie maszyny wirtualne, interpretują kod bajtowy. Nawet JVM faktycznie to robi, przynajmniej przed uruchomieniem JIT (just-in-time): interpretuje kody bajtów i wykonuje instrukcje w języku, w którym JVM został napisany (zazwyczaj C lub C ++, ale jest nawet jeden napisany w JavaScript, Doppio ). Zauważ jednak, że nawet takie instrukcje zostały przetłumaczone na kod maszynowy przez kompilator i faktycznie wyglądają bardzo podobnie do tego, co wytwarza kompilator Java - mianowicie używają rejestrów i stosu do wykonywania swojej pracy. Zauważ, że użycie języków „interpretowanych” a „skompilowanych” staje się w tym momencie nieco rozmyte .
źródło
Dlaczego maszyny wirtualne muszą być „maszynami stosowymi” lub „maszynami rejestrującymi” itp.?
Oni nie. Jeśli potrzebujesz maszyny wirtualnej, może to być cokolwiek.
Istniejące maszyny wirtualne pojawiły się jako rozwiązania takich sytuacji: Naprawdę genialny pomysł przyszedł mi do głowy, wymyśliłem nowy język programowania! Ale muszę wygenerować kod. (Co za nudne zadanie!) Ale nie chcę generować kodu i8086, ponieważ jest brzydki, i nie chcę generować kodu 68k, ponieważ wszyscy inni używają Intela. Istnieje również VAX, ale nie mam żadnego VAX, ani komputera, ani książki VAX. Dlatego wygeneruję kod dla jakiegoś procesora, który fizycznie nie istnieje i zaimplementuję ten procesor w oprogramowaniu. Specyfikacja tej maszyny wirtualnej stworzy rozdział w mojej pracy magisterskiej. Teoretycznie możliwe będzie skompilowanie go do natywnego kodu dowolnego procesora, ale to nie będę ja.
Z drugiej strony, notacja typu „2 + 3” prawdopodobnie nie będzie używana przez maszyny wirtualne w dającej się przewidzieć przyszłości, ponieważ oznacza to konieczność przeprowadzenia dużej transformacji, zanim coś zostanie wykonane.
źródło
Aby odpowiedzieć na zadane pytanie. Termin „wirtualna maszyna” oznacza, że WSZYSTKIE oprogramowanie / sprzęt są symulowane / emulowane. Jeśli korzystasz z oprogramowania / sprzętu, aby wykonać instrukcje, nie masz maszyny wirtualnej, masz kompilator / interpreter.
źródło