Почему виртуальные машины должны быть «стековыми машинами» или «регистрировать машины» и т. Д.?

48

(Это чрезвычайно новый вопрос).

Я немного изучал виртуальные машины.

Оказывается, многие из них спроектированы очень похоже на физические или теоретические компьютеры.

Я читал, что JVM, например, является «машиной стека». Это означает (и исправьте меня, если я ошибаюсь), что он хранит всю свою «временную память» в стеке и выполняет операции с этим стеком для всех своих кодов операций.

Например, исходный код 2 + 3будет преобразован в байт-код, подобный следующему:

push 2
push 3
add

У меня вопрос такой:

JVM, вероятно, написаны с использованием C / C ++ и тому подобного. Если так, почему JVM не выполняет следующий код C: 2 + 3..? Я имею в виду, зачем ему стек или другие регистры виртуальных машин - как на физическом компьютере?

Основной физический процессор заботится обо всем этом. Почему пишущие виртуальные машины просто не выполняют интерпретируемый байт-код с «обычными» инструкциями на языке, на котором программируется виртуальная машина?

Зачем виртуальным машинам эмулировать аппаратное обеспечение, если реальное аппаратное обеспечение уже делает это для нас?

Опять же, очень новые вопросы. Спасибо за вашу помощь

Авив Кон
источник
5
Рассматривали ли вы, на чем основаны не виртуальные машины?
5
@MichaelT Вы имеете в виду физические машины?
Авив Кон
Конечно, большинство виртуальных машин Javascript не являются стековыми или регистрирующими машинами - V8 / IonMonkey / Chakra / и т. Д. Являются виртуальными машинами, которые реализуют Javascript. «VM» - это просто интерпретатор или JIT-компилятор, который может реализовывать любой язык, который так желает разработчик.
Билли ОНил
@BillyONeal Так, например, если я пишу виртуальную машину для какого-то языка и пишу ее на языке C: виртуальная машина анализирует строку байт-кода «print» hi »и выполняет printf("hi");: считается ли это виртуальной машиной ? У него нет ни «стека», ни «регистров», ни чего-либо еще.
Авив Кон
@Prog: Да, это правильно.
Билли ONEAL

Ответы:

51

Машина, виртуальная или нет, нуждается в модели вычислений, которая описывает, как вычисления выполняются на ней. По определению, как только он вычисляет, он реализует некоторую модель вычисления. Тогда возникает вопрос: какую модель выбрать для нашей виртуальной машины? Физические машины ограничены тем, что может быть эффективно и действенно сделано аппаратно. Но, как вы заметили, виртуальные машины не имеют таких ограничений, они определяются в программном обеспечении с использованием языков произвольно высокого уровня.

На самом деле, как вы описываете, существуют виртуальные машины высокого уровня. Они называются языками программирования . Например, стандарт C посвящает большую часть своих страниц определению модели для так называемой «абстрактной машины C», которая описывает, как ведут себя программы на C, и в качестве расширения (как правило), как соответствующий компилятор C (или интерпретатор). должен вести себя

Конечно, мы обычно не называем это виртуальной машиной. Под виртуальной машиной обычно понимается нечто более низкое, более близкое к аппаратному, не предназначенное для непосредственного программирования, предназначенное для эффективного выполнения. Это смещение выбора означает, что то, что принимает высокоуровневый составной код (например, то, что вы описываете), не будет считаться виртуальной машиной, поскольку выполняет высокоуровневый код.

Но чтобы перейти к сути, вот несколько причин сделать виртуальную машину (например, что-то, предназначенное для компилятора байт-кода) регистровой или тому подобное. Укладывать и регистрировать машины чрезвычайно просто. Для каждой инструкции есть последовательность инструкций, некоторое состояние и семантика (функция State -> State). Нет сложных сокращений дерева, нет приоритета оператора. Синтаксический анализ, анализ и выполнение очень просты, потому что это минимальный язык (синтаксический сахар компилируется) и предназначен для машинного чтения, а не человеческого чтения.

Напротив, анализ даже самых простых C-подобных языков довольно сложен, и для его выполнения требуются нелокальные анализы, такие как проверка и распространение типов, разрешение перегрузок, поддержка таблицы символов, разрешение строковых идентификаторов, превращение линейного текста в AST на основе приоритетов. , и так далее. Он основан на концепциях, которые естественны для человека, но должны быть тщательно обработаны машинами.

Байт-код JVM, например, излучается javac. Его практически никогда не нужно читать или писать людям, поэтому естественно ориентировать его на потребление машинами. Если вы оптимизировали его для людей, виртуальная машина будет только при каждом запуске читать код, анализировать его, анализировать это, а затем преобразовать его в промежуточное представление , напоминающее такая модель упрощенной машины в любом случае . С таким же успехом можно вырезать среднего человека.


источник
Итак, что вы говорите, что компиляция всего в инструкции в стеке (то System.out.println("hi");есть компилируется в какую-то инструкцию в стеке, int a = 7компилируется в инструкцию в стеке и т. Д.) Делает выполнение программы простым и более эффективным?
Авив Кон
2
@ В основном да. Но не только исполнение, но и анализ. Все, что делается программно.
Тем не менее, я не понимаю, почему 2 + 3компилируется в push 2 push 3 add. addШаг в конце выполняется с помощью виртуальной машины Java в любом случае, запустив код C 2 + 3. У программистов JVM нет другого способа сделать это. Почему бы не скомпилировать его 2 + 3и заставить JVM просто выполнить код C 2 + 3(если он написан на C) прямо сейчас?
Авив Кон
@Prog Автор JVM не может просто написать 2 + 3исходный код JVM, потому что JVM должен работать с любой программой, выполняющей любые операции в любом порядке. Создание исходного кода на C и откладывание до реализации на C просто выдвигает ту же проблему в реализацию на C (и не может быть сделано легко, не говоря уже об эффективности). Должна существовать некоторая структура данных, описывающая программу, чтобы ее можно было интерпретировать и скомпилировать JIT, а «читаемый исходный код» является ужасным выбором структуры данных по причинам, изложенным выше.
7
@Prog Вы, кажется, слишком сосредоточены на конкретном случае 2 + 3. Как насчет a + b? Тогда добавляемые значения не приходят i.argument{1,2}, они загружаются из локальных переменных. Как насчет frobnicate(x[i]) + (Foo.bar() * 2)? Используя этот дизайн, есть только одна addоперация (для int), и она работает независимо от того, как были вычислены добавления. Плюс, инструкция, которая добавляет только целочисленные литералы, была бы бессмысленной: ее результат также можно было бы предварительно вычислить (т. Е. Вместо add(2,3)этого должен быть push(5)).
20

Этот ответ фокусируется на JVM, но на самом деле он применим к любой виртуальной машине.

Зачем виртуальным машинам эмулировать аппаратное обеспечение, если реальное аппаратное обеспечение уже делает это для нас?

Это не так, но это делает виртуальную машину намного проще и портативнее: виртуальная машина, которая эмулирует аппаратное обеспечение, может использовать ту же вычислительную модель, что и любой аппаратный процессор.

В частности, JVM была разработана с учетом переносимости, фактически она была построена таким образом, чтобы ее можно было даже реализовать на аппаратном уровне (сегодня может быть трудно поверить, но происхождение Java было во встроенном мире - в частности, контроллеры для интерактивного телевидения). ).

Если у вас есть цель, подобная этой, желательно, чтобы виртуальная машина работала как можно ближе к физической машине, поскольку преобразование в реальный машинный код становится проще и, следовательно, быстрее. Теоретически, когда у вас есть коды операций виртуальной машины, все, что вам нужно сделать, это перевести на коды операций процессора, на котором фактически работает программа. На практике все не так просто.

Я имею в виду, зачем ему стек или другие регистры виртуальных машин - как на физическом компьютере?

Преимущество использования модели виртуальной машины, основанной на стеке, заключается в том, что ее можно легко переносить как на машины регистрации, так и на машины стека, тогда как обратное не всегда верно. Виртуальная машина на основе регистров должна делать предположения о количестве регистров, размере регистров и т. Д. При использовании стекового компьютера такие предположения не требуются.

Основной физический процессор заботится обо всем этом. Почему пишущие виртуальные машины просто не выполняют интерпретируемый байт-код с «обычными» инструкциями на языке, на котором программируется виртуальная машина?

Ну, вот что делают такие виртуальные машины, они интерпретируют байт-код. Даже JVM фактически делает это, по крайней мере, до того, как JIT (как раз вовремя) начинает работать: он интерпретирует байтовые коды и выполняет операторы на языке, на котором написана JVM (обычно C или C ++, но есть даже один написанный в JavaScript, Доппио ). Обратите внимание, однако, что даже такие операторы были преобразованы в машинный код компилятором и на самом деле выглядят очень похоже на то, что производит компилятор Java, а именно они используют регистры и стек для выполнения своей работы. Обратите внимание, что использование «интерпретированных» и «скомпилированных» языков становится несколько размытым в этой точке.

miraculixx
источник
Конечно, все, что может быть реализовано программно, может быть реализовано аппаратно. Кроме того, JVM в настоящее время (горячая точка) является JIT-компилятором - он не выполняет операторы на языке, на котором была написана JVM. Если бы это было так, Java работала бы ужасно и не была бы столь же жизнеспособной платформой, как сегодня , (Черт, большинство реализаций Javascript будет быстрее)
Billy ONeal
2
@BillyONeal "Вместо того, чтобы компилировать метод по методу, как раз вовремя, виртуальная машина Java HotSpot немедленно запускает программу с использованием интерпретатора и анализирует код во время работы, чтобы обнаружить критические горячие точки в программе. Затем она фокусирует внимание глобальный оптимизатор нативного кода в горячих точках. " Цитата: oracle.com/technetwork/java/whitepaper-135217.html#2 , раздел" Обнаружение горячих точек "
miraculixx
Да. "Оптимизатор собственного кода" == JIT-компиляция. Для кода существует фаза интерпретатора, которая не выглядит «горячей», чтобы избежать JIT-кода редко используемых вещей. Но это не значит, что JITing не делается вообще.
Билли ONEAL
Спасибо за ответы. Что я понял из вашего ответа, так это то, что причины для эмуляции аппаратного обеспечения в ВМ (то есть с помощью «стеков» или «регистров» и т. Д.) Заключаются в том, что это облегчает последующую компиляцию байт-кода или исходного кода в фактический машинный код физический процессор. Однако помимо этого - есть ли что-то, что можно выиграть от эмуляции оборудования в виртуальной машине? Я до сих пор не понимаю, почему кто-то, проектирующий ВМ, думал бы о терминах «машина стека» или «машина регистрации» и т. Д., Хотя на самом деле мы говорим о программном обеспечении. Я что-то пропустил?
Авив Кон
@Prog Хорошо, у вас есть язык программирования, скажем X. Как вы будете запускать его программы? Вы можете либо интерпретировать исходный код, либо скомпилировать его в машинный код, либо скомпилировать его в некоторый промежуточный код. Теперь у вас есть другой язык программирования Y, и вы хотите реализовать его с помощью X. Если обе реализации являются интерпретатором, у вас будет интерпретатор Y, работающий на интерпретаторе X, и это будет очень медленно.
18446744073709551615
11

Почему виртуальные машины должны быть «стековыми машинами» или «регистрировать машины» и т. Д.?

Они не. Если вам нужна виртуальная машина, это может быть что угодно.

Существующие виртуальные машины появились в качестве решения таких ситуаций, как: мне пришла в голову действительно блестящая идея, я изобрел новый язык программирования! Но я должен генерировать код. (Какая скучная задача!) Но я не хочу генерировать код i8086, потому что он уродлив, и я не хочу генерировать код 68k, потому что все остальные используют Intel. Также есть VAX, но у меня нет ни VAX, ни компьютера, ни книги по VAX. Поэтому я сгенерирую код для некоторого процессора, который физически не существует, и реализую этот процессор в программном обеспечении. Спецификация этой ВМ станет главой в моей диссертации. Теоретически, будет возможно скомпилировать его для нативного кода любого процессора, но это не я.

С другой стороны, обозначения типа «2 + 3», вероятно, не будут использоваться виртуальными машинами в обозримом будущем, потому что они предполагают выполнение большого количества преобразований, прежде чем что-либо может быть выполнено.

18446744073709551615
источник
Спасибо за ответы. Итак, что я понял из вашего ответа, так это то, что мотивация для создания виртуальной машины, которая имитирует физические процессоры, заключается в том, что она облегчает последующую реализацию компиляторов, которые компилируются в реальный машинный код. Но кроме этого - есть ли какие-либо преимущества в проектировании виртуальной машины с точки зрения «машины стека» или «машины регистрации» и т. Д.?
Авив Кон
1
Для регистров требуются алгоритмы распределения регистров, которые требуют как теории, так и отладки. Машина стека (особенно с нулевым операндом) может просто поместить данные в стек. OTOH, аппаратное обеспечение обычно реализует ограниченное количество регистров, а не стек переменного размера. Так что стеки проще для программного обеспечения, регистры проще для аппаратного обеспечения и, следовательно, немного быстрее.
18446744073709551615
-2

Чтобы ответить на фактический вопрос, который был задан. Термин «виртуальная МАШИНА» означает, что ВСЕ программное / аппаратное обеспечение моделируется / эмулируется. Если вы используете основное программное / аппаратное обеспечение для выполнения инструкций, то у вас нет виртуальной машины, у вас есть компилятор / интерпретатор.

Kyrelel
источник
это просто ваше мнение или вы можете как-то это подтвердить?
комар
@ Кайрелел, это неправда. «ВСЕ» оборудование эмулируется в «системной» или «полной» виртуальной машине. Не все виртуальные машины заполнены. Например, уровень BSD VM называется «виртуальная машина», несмотря на то, что оборудование там не эмулируется.
Нетч
Я не думаю, что вопрос обязательно связан с терминологией, а скорее с тем, почему виртуальные машины реализуют функциональность, которая, по-видимому, уже обрабатывается фактическим оборудованием
Райан,