Зачем нужны виртуальные машины?

Я читал этот вопрос чтобы узнать различия между виртуальной машиной Java и .NET CLR и ответом Бенджи заставил меня задаться вопросом, почему виртуальные машины необходимы в первую очередь.

из моего понимания объяснения Бенджи, JIT-компилятор виртуальной машины интерпретирует промежуточный код в фактический код сборки, который работает на CPU. Причина этого заключается в том, что процессоры часто имеют разное количество регистров и согласно Бенджи, " некоторые регистры являются специальными, и каждая инструкция ожидает свои операнды в разных регистрах."Тогда имеет смысл, что существует необходимость в промежуточном интерпретаторе, таком как виртуальная машина, чтобы один и тот же код мог быть запущен на любом процессоре.

но, если это так, то я не понимаю, почему код C или c++, скомпилированный в машинный код, может работать на любом компьютере, если это правильная ОС. Почему тогда программа C, которую я скомпилировал на моя машина Windows, использующая Pentium, может работать на другой машине Windows с помощью AMD?

Если код C может работать на любом процессоре, то какова цель виртуальной машины? Это значит, что один и тот же код может быть запущен на любой ОС? Я знаю, что Java имеет версии VM практически на любой ОС, но есть ли CLR для других ОС, кроме Windows?

или есть что-то еще, чего мне не хватает? Делает ли ОС какую-либо другую интерпретацию кода сборки, который она запускает, чтобы адаптировать его к конкретному процессору или что-то?

Мне очень любопытно, как все это работает, поэтому четкое объяснение было бы очень признательно.

Примечание: причина, по которой я не просто разместил свои запросы в качестве комментариев в вопросе JVM vs. CLR, заключается в том, что у меня недостаточно очков для публикации комментариев =b.

Edit: Спасибо за все отличные ответы! Кажется, мне не хватало того, что, хотя все процессоры имеют различия, существует общая стандартизация, в первую очередь архитектура X86, который предоставляет достаточно большой набор общих функций, так что код C, скомпилированный на одном процессоре X86, будет работать по большей части на другом процессоре X86. Это способствует оправданию виртуальных машин, не говоря уже о том, что я забыл о важности сбора мусора.

10 ответов


процессоры AMD и intel используют один и тот же набор инструкций и машинную архитектуру (с точки зрения выполнения машинного кода).

компиляторы C и c++ компилируются в машинный код с заголовками, соответствующими ОС, на которую они нацелены. После компиляции они перестают ассоциироваться каким-либо образом, формой или формой с языком, на котором они были скомпилированы, и являются просто двоичными исполняемыми файлами. (есть артефакты, которые могут показать, с какого языка он был скомпилирован, но это не точка здесь)

таким образом, после компиляции они связаны с машиной (X86, набором инструкций intel и amd и архитектурой) и ОС.

вот почему они могут работать на любой совместимой машине x86 и любой совместимой ОС (win95 через winvista, для некоторых программ).

однако они не могут работать на компьютере OSX, даже если он работает на процессоре intel - двоичный файл не совместим, если вы не запустите дополнительное программное обеспечение эмуляции (например, parallels или VM с Windows.)

кроме того, если вы хотите запустить их на процессоре ARM, или MIPS, или PowerPC, то вам нужно запустить полный эмулятор набора команд машины, который интерпретирует двоичный машинный код из X86 в любую машину, на которой вы его запускаете.

противопоставить .Нет.

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

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

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

теперь у вас может быть C, C#, C++, Java, javascript, Basic, python, lua или любой другой компилятор языка, который преобразует написанный код, чтобы он работал на этой виртуальной машине.

вы отделили машину от языка на 2 градуса, и с не слишком большой работой вы позволяете кому-либо писать любой код и иметь он работает на любой машине, пока существует компилятор и виртуальная машина для сопоставления двух степеней разделения.

Если вы все еще задаетесь вопросом, почему это хорошо, рассмотрите ранние машины DOS и что Microsoft реальные вклад в мир был:

Autocad должен был писать драйверы для каждого принтера, на который они могли печатать. Как и lotus 1-2-3. Фактически, если вы хотите, чтобы ваше программное обеспечение печаталось, вы должны были написать свои собственные драйверы. Если есть 10 принтеров и 10 программы, затем 100 различных частей по существу одного и того же кода должны были быть написаны отдельно и независимо.

то, что windows 3.1 пыталась выполнить (вместе с GEM и многими другими слоями абстракции), - это сделать так, чтобы производитель принтера написал один драйвер для своего принтера, а программист написал один драйвер для класса принтера windows.

теперь с 10 программами и 10 принтерами, только 20 частей кода должны быть написаны, и так как сторона microsoft код был одинаковым для всех, тогда примеры из MS означали, что у вас было очень мало работы.

теперь программа была ограничена не только 10 принтерами, которые они выбрали для поддержки, но и всеми принтерами, производители которых предоставили драйверы для windows.

та же проблема возникает при разработке приложений. Есть очень аккуратные приложения, которые я не могу использовать, потому что я не использую MAC. Существует тонна дублирования (сколько текстовых процессоров мирового класса мы действительно нужно?).

Java должен был исправить это, но у него было много ограничений, некоторые из которых на самом деле не решены.

.NET ближе, но никто не разрабатывает виртуальные машины мирового класса для платформ, отличных от Windows (mono так близко... и все же не совсем там).

Так... Вот почему нам нужны VMs. Потому что я не хочу ограничивать себя небольшой аудиторией просто потому, что они выбрали комбинацию ОС/машины, отличную от моей.

Адам


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

однако есть определенные сходства, которые разделяют процессоры, например, процессоры Intel x86 и процессоры AMD имеют достаточно большой набор свойств, которые большинство кода, скомпилированного против одного, будут работать на другом. Однако, если вы хотите использовать свойства процессора, то вам нужен компилятор или набор библиотек, которые сделают это за вас.

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

наиболее заметным сервисом является сбор мусора, предлагаемый CLR и JVM. Оба эти виртуальные машины предлагают вам эту услугу бесплатно. Они управляют памятью за вас.

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

CLR также предлагает форму безопасности кода для вас.

ни один из них не предложил в рамках базовой среды выполнения для ряда других языков, которые не работают с виртуальной машиной.

вы можете получить некоторые из них используя библиотеки, но тогда это заставляет вас использовать шаблон с библиотекой, тогда как в .NET и Java-сервисах, которые предлагаются Вам через CLR и JVM, согласованы в их доступе.


по сути, это позволяет "управляемый код", что означает именно то, что он говорит - виртуальная машина управляет кодом по мере его запуска. Три основных преимущества этого-своевременная компиляция, управляемые указатели / сбор мусора и контроль безопасности.

для компиляции just-in-time виртуальная машина наблюдает за выполнением кода, и так как код запускается чаще, он повторно запускается быстрее. Вы не можете сделать это с помощью собственного кода.

управляемые указатели кроме того, их легче оптимизировать, поскольку виртуальная машина отслеживает их по ходу движения, управляя ими по-разному в зависимости от их размера и срока службы. Это трудно сделать на C++, потому что вы не можете сказать, куда указатель собирается идти, просто читая код.

безопасность не требует пояснений, виртуальная машина останавливает код от того, что он не должен делать, потому что он смотрит. Лично я думаю, что это, вероятно, самая большая причина, почему Microsoft выбрала управляемый код для C#.

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


большинство компиляторов, даже собственные компиляторы кода, используют какой-то промежуточный язык.

Это в основном делается для снижения затрат на построение компилятора. В мире существует много (N) языков программирования. В мире также существует множество (M) жестких платформ. Если бы компиляторы работали без использования промежуточного языка, общее число "компиляторов", которые должны были бы быть написаны для поддержки всех языков на всех аппаратных платформах, было бы N*M.

, по определение промежуточного языка и разбиение компилятора на 2 части, передний конец и задний конец, причем передний конец компилирует исходный код в IL, а задний конец компилирует IL в машинный код, вы можете уйти от написания только N+M компиляторов. Это приводит к огромной экономии.

большая разница между компиляторами CLR / JVM и компиляторами собственного кода заключается в том, как компиляторы переднего и заднего концов связаны друг с другом. В собственном компиляторе кода два компоненты обычно объединяются в один и тот же исполняемый файл, и оба запускаются, когда программист нажимает "build" в IDE.

с компиляторами CLR / JVM передняя и задняя части выполняются в разное время. Передняя часть запускается во время компиляции, производя IL, который фактически поставляется клиентам. Задняя часть затем воплощается в отдельном компоненте, который вызывается во время выполнения.

Итак, это поднимает альтернативный вопрос: "каковы преимущества задержки back end компиляции до времени выполнения"?

ответ: "Это зависит".

задерживая компиляцию до выполнения, становится возможным отправить один набор двоичных файлов, которые могут работать на нескольких аппаратных платформах. Это также позволяет программам использовать преимущества улучшений в технологии компиляции back end без перераспределения. Он также может обеспечить основу для эффективной реализации многих динамических языковых функций. Наконец, он предлагает возможность ввести ограничения безопасности и надежности между отдельно скомпилированными динамически связанными библиотеками (DLL), что невозможно при компиляции машинного кода.

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


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

типы разработчиков всегда добавляли дополнительные слои абстракций, потому что о силе и особенностях, которые они приносят. Ведь лучшие абстракции позволяют писать новые приложения быстрее и надежнее. Компании не заботятся о том, что или как они код выглядит, как они просто хотят, чтобы работа была сделана надежно и быстро. Имеет ли значение, если версия приложения C занимает несколько миллисекунд меньше, но в конечном итоге занимает вдвое больше времени для разработки ?

вопрос скорости почти не аргумент, как многие корпоративные приложения, которые обслуживают миллионы люди написаны на платформах / языках, таких как java - например, GMail, GMaps. Забудьте о том, какой язык/платформа является самым быстрым. Что более важно, так это то, что вы используете правильные алгоритмы и пишете эффективный код и выполняете работу.


процессоры AMD и Intel имеют архитектуру x86, если вы хотите запустить программу c/C++ на другой архитектуре, вы должны использовать компилятор для этой архитектуры, один и тот же двоичный исполняемый файл не будет работать на разных архитектурах процессора.


Я знаю, что Java имеет версии VM практически на любой ОС, но есть ли CLR для других ОС, кроме Windows?

моно


очень упрощенным способом, потому что Intel и AMD реализуют один и тот же язык сборки с одинаковым количеством регистров и т. д...

таким образом, ваш компилятор C компилирует код для работы в Linux. Эта сборка использует Linux ABI, так как программа компиляции запускается в Linux, на сборке x86 и правильной сигнатуре функции, то все в порядке.

теперь попробуйте взять этот скомпилированный код и вставить его, скажем Linux / PPC (например, Linux на старые и iBook). Это не сработает. Где как Java-программа, потому что JVM был реализован на платформе Linux/PPC.

сборка langauge в настоящее время в основном еще один интерфейс, который программист может запрограммировать. x86 (32-бит) позволяет получить доступ к eax,ebx,ecx,edx для целочисленных регистров общего назначения и f00-f07 для плавающей точки. За кулисами у CPU на самом деле есть еще сто регистров, и они перемешаны, чтобы выжать производительность.


вы правы в своем анализе, java или C# могли быть разработаны для компиляции direct для запуска на любой машине и, вероятно, были бы быстрее, если бы они это сделали. Но подход виртуальной машины дает полный контроль над средой, в которой выполняется ваш код, виртуальная машина создает безопасную песочницу, которая позволяет только командам с правильным доступом к безопасности выполнять потенциально повреждающий код, как изменение пароля, или обновление HD bootsector. Есть много других преимуществ, но это убийственная причина. Вы не можете получить StackOverflow в C# ...


Я думаю, что посылка вашего вопроса верна - вы, конечно, не первый, кто задает этот вопрос. Так что проверьтеhttp://llvm.org чтобы увидеть альтернативный подход (который теперь выполняется проект? или спонсируется Apple)