Технологии виртуализации вчера, сегодня, завтра

         

Другие подходы к виртуализации. Виртуальная машина Xen


Проект Xen (произносится как «Зен») - пожалуй, самый динамично развивающийся и современный пакет виртуализационного ПО, яркий пример того, на что, при соответствующей поддержке, способно сообщество Open Source. Начатый Кэмбриджским университетом как открытая реализация относительно несложной технологии паравиртуализации, Xen вскоре стал одним из наиболее популярных виртуализационных проектов, и получил богатейшую функциональность, включающую в себя систему обеспечения взаимной безопасности виртуальных машин, систему управления их ресурсами, систему обеспечения «гарантированного уровня обслуживания» (Quality of Service, QoS), систему «незаметной миграции» (за 50-300 мс возможно «перебросить» работающую виртуальную систему с одного физического компьютера на другой), и многое другое. Как и любое другое ПО, реализующее технологию паравиртуализации, Xen выступал в качестве прослойки между операционными системами и физическим оборудованием, и требовал, чтобы операционная система была адаптирована к работе не с реальным «железом», а с этой виртуализационной прослойкой. Соответствующие патчи, обеспечивающие необходимую поддержку для Xen со стороны операционной системы были разработаны для Linux, FreeBSD, NetBSD и экзотической Plan 9, и многие крупные вендоры включили эту поддержку, вместе с самим Xen, в свои дистрибутивы соответствующих операционных систем. И всё это - за два года, с 2003 по 2005 год!

Следующий этап развития проекта был связан с именем Intel, решившей использовать Xen в качестве основного «популяризатора» своей технологии виртуализации VT. Разработчики Intel дописали для Xen соответствующий модуль, обеспечивающий сопряжение на VT-совместимых процессорах произвольной ОС с внутренним интерфейсом Xen. Модуль был включен в общий проект, и таким образом Xen «неожиданно» обрёл способность работать с произвольными операционными системами - благо, что вся необходимая для этого инфраструктура в проекте уже присутствовала.
AMD, тоже не осталась в стороне, от данного вопроса, и к настоящему моменту Xen получил экспериментальную поддержку и технологии аппаратной виртуализации Pacifica, еще не «включённой» ни в одном из продаваемых ныне процессоров AMD, но зато более современной и более удобной с точки зрения реализации. А поскольку «родительской» операционной системы для Xen не требуется, то вот так, в одночасье, из игрушки сообщества Open Source, этот проект превратился в бесплатный универсальный менеджер виртуальных машин для новейших процессоров AMD и Intel, пригодный для использования широким кругом пользователей. Скорее всего, благодаря активной поддержке обоих «грандов процессоростроения», именно Xen, а не продукция Microsoft или VMWare, ляжет в основу будущего стандарта на VMM и станет «традиционным выбором пользователей». К сожалению, случится это, похоже, в сравнительно отдалённом будущем, ибо установить, настроить, и заставить как-то работать Xen прямо сейчас сможет, боюсь, далеко не каждый даже довольно опытный пользователь.

Технические характеристики Xen выглядят следующим образом: поддерживаются все специально адаптированные к Xen операционные системы, либо любые x86-совместимые операционные системы (Itanium-совместимые - в стадии бета-версии Xen) при наличии средств аппаратной поддержки виртуализации (Intel VT-x «Vanderpool» / AMD SVM «Pacifica»). На момент написания статьи (Xen 3) для установки Xen-а требовалось наличие работающей версии Linux с загрузчиком GRUB, а конфигурация проводилась ручной правкой конфигурационных файлов; причём Xen включал в себя самостоятельное ядро Linux, загружаемое в этой «родной» системе взамен основного, что, к примеру, могло потребовать перекомпиляции для этого ядра имеющихся в системе модулей LKM. Для дистрибутивов, в которые Xen изначально был включён, особой проблемы подобное своеобразие установки не создаст (в SuSe Linux Professional 10 управление Xen-ом было даже включено в графическую утилиту YAST Control Center), всем остальным - придётся дожидаться выхода соответствующих пригодных к использованию обычным пользователем пакетов.Правда, к сожалению, даже тогда всерьез запустить на платформе Xen операционную систему MS Windows удастся лишь с большим скрипом - предоставляемые Xen возможности по имитированию оборудования виртуального компьютера сегодня, мягко говоря, недоразвиты, а работать по сетевому протоколу из-под Linux с запущенной где-то там, в глубинах компьютера, MS Windows, доступной разве что в виде виртуального сетевого хоста, на котором из «железа» присутствует лишь жёсткий диск, процессор, оперативная память, да сетевая карточка, задача не из тривиальных. Юниксоида такой набор устроит вполне, «домашнего пользователя» - вряд ли.

Однако это вряд ли сильно помешает светлому будущему Xen: с драйверами Intel и AMD проекту наверняка поспособствуют, а появление удобного для конечного пользователя дистрибутива, устанавливаемого на «голую» или даже уже работающую машину - это лишь вопрос времени. Подождём ближе к концу 2006 года Xen версии 4?


Эмуляторы виртуальных машин


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

У эмуляторов очень много преимуществ. Реализуемые ими виртуальные машины могут быть сколь угодно сложны и, что важнее, принципиально отличаться от реальной физической машины, средствами которой они поддерживаются. Одно и то же Java-приложение может быть запущено практически на любом «железе»; эмулятор Spectrum позволяет выполнять приложения, написанные для процессора Z80 на процессорах архитектуры x86; и т.д. Классические виртуализаторы всего этого делать, увы, не позволяют, - запустить, скажем, на x86, приложение для MacOS (использующее архитектуру PowerPC) с их помощью принципиально невозможно.

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


Несколько раз предпринимались серьезные попытки исправить данное положение дел, отказавшись от исполнения кода «на лету», когда эмулятор последовательно, инструкция-за-инструкцией, транслирует программу, и перейдя к «динамической компиляции программ», когда программа, записанная в одной системе команд предварительно «переводится» в «родную» систему команд данного процессора, и уже затем, в виде полученного «родного» кода на этом процессоре исполняется. К примеру, разработанный Connectix, позднее купленной Microsoft, продукт Virtual PC for Macintosh позволял, за счёт подобного «перекомпилирования» приложений для операционных систем Microsoft, запускать эти приложения на компьютерах Apple Macintosh. А компания Transmeta в 1999 году даже выпустила совершенно уникальный процессор Crusoe (VLIW-архитектуры), который имитировал «видимость» x86-архитектуры при помощи специального полуаппаратного эмулятора, разработанного, к слову, с участием Линуса Торвальдса. А позднее Microsoft разработала на основе данного подхода и «усовершенствованную альтернативу» Java - технологию .Net, использующую для записи программ специальный «универсальный код» CIL (Common Intermediate Language), который по своей сути аналогичен псевдокоду, который генерируют в ходе своей работы современные компиляторы перед тем, как сконвертировать этот «абстрактный код» во вполне конкретные машинные инструкции.

Потенциально данный подход лишён всех «узких мест», связанных с недостаточной производительностью обычных эмуляторов, однако технология .Net до сих пор так и не получила обещанного распространения, а производительность Virtual PC for Macintosh, равно как и Transmeta Crusoe, оставляет желать лучшего.


Microsoft VirtualPC/Virtual Server


В отличие от VMWare, Microsoft никогда толком не разрабатывала собственных систем виртуализации: выпускаемые сегодня под её брендом VirtualPC и Virtual Server изначально были разработаны компанией Connectix. Но в 2003 году Microsoft скупила данные продукты у Connectix, что называется, «на корню», и с тех пор примерно та же команда разработчиков выпускает лишь слегка «подрихтованные» бывшие продукты Connectix под Microsoft-овской маркой. Одной из сторон подобного перехода «под крыло» Microsoft стало то, что отныне VirtualPC работает исключительно под управлением десктопных версий ОС Windows XP/2000, а более функциональный Virtual Server - и вовсе только под управлением серверных Windows XP/2003 Server.

Изначально виртуализационное ПО Microsoft было ориентировано на использование технологии бинарной трансляции кода. Исключение - VirtualPC for Macintosh, который формально также использует ту же технологию бинарной трансляции, но по сути своей является, скорее, продвинутым эмулятором (см. ниже). В 2005 году Microsoft также заявила о поддержке в своих будущих продуктах технологий Intel VT-x и AMD SVM «Pacifica», однако бета-версии соответствующих продуктов выйдут лишь в первой половине 2006 года, а окончательный релиз - во второй половине.

В части оборудования VirtualPC и Virtual Server имитируют один и тот же «стандартный компьютер» с процессором Pentium II (с поддержкой MMX), работающий на чипсете Intel 440BX, с видеокартой S3 Trio 64 PCI (с 4 Mb видеопамяти), BIOS от American Megatrends (AMI), звуковой картой Creative Sound Blaster 16 PnP (Virtual Server её не поддерживает), и сетевой картой DEC 21041 / 21040. Конфигурация хоть и старенькая, но весьма распространённая в своё время, а потому имеющая очень неплохую поддержку со стороны программного обеспечения.

Официально заявленная Microsoft поддержка гостевых операционных систем (другие системы также могут работать) представлена в .





Паравиртуализация и бинарная трансляция


Итак, как мы уже сказали, все пользовательские приложения сегодня, фактически, работают на «виртуальных» компьютерах - им предоставляется некая «обобщенно-стандартная» среда исполнения с виртуальной оперативной памятью, и с этим «виртуальным компьютером» они свободно работают, не задумываясь о том, какие реальные физические ресурсы за этой виртуальностью стоят. Центральная задача операционной системы - это поддержание этой «виртуальной реальности» и своевременное распределение между этими виртуальностями реальных аппаратных ресурсов. Сама операционная система тоже живёт на одном из «виртуальных компьютеров», но, в отличие от всех остальных «обитателей» компьютера, обладает возможностью свою (и чужие) «реальности» изменять и соотносить с физическими ресурсами компьютера.

И уже сама по себе подобная возможность позволяет, на самом деле, реализовывать практически всё, что угодно, с пользовательскими приложениями. К примеру, потенциально можно взять, «сохранить» состояние приложения на флэшку, «скопировать» на другой компьютер и «продолжить» выполнение программы уже на другом компьютере. Можно (потенциально) запускать в одной и той же операционной системе как Windows, так и POSIX-приложения (Linux, Unix-системы) - достаточно уметь создавать два «типа» виртуальных компьютеров, чтобы каждое приложение получало ровно ту среду исполнения, в которой оно привыкло работать. Но, к сожалению, для пользователя, подобные «хитрости», требующие активной поддержки со стороны операционной системы, реализовать на практике далеко не так просто, как рассказать о них. И обеспечить, скажем, «родную» поддержку Windows-приложений в Linux, равно как и обратную поддержку Linux-приложений в Windows, по причине активного противодействия Microsoft, невозможно. А потому пользователь вынужден обходиться без некоторых интересных функций и довольствоваться Windows-приложениями на Windows-системах и Linux-приложениями на Linux-системах.


Идея здесь очень простая: используя виртуальную память, мы можем сымитировать виртуальный компьютер практически любой сложности: так что «гостевой» операционной системе попросту «подсовывается» виртуальная машина, очень напоминающая «физическую» x86-машину. «Гость» принимает «обманку» за настоящий компьютер - и вполне успешно начинает на этой виртуальной машине, имитируемой «родительской» ОС, работать. Обратите внимание, на то, что это не подход, аналогичный «виртуальной машине Java» или эмуляторам древнего Sinclair, когда приложение-эмулятор виртуальной машины «вручную» разбирает код приложения и «вручную» же исполняет каждую его инструкцию. Гостевая операционная система и все запущенные в её рамках приложения работают на физических ресурсах компьютера практически так же, как это делает обычное запущенное на нём приложение, а «виртуализирующее приложение» только обеспечивает контроль над ним - тонюсенькая прослойка кода, поддержанная стандартными аппаратными ресурсами компьютера. Давайте разберём немножко подробнее, как такое оказывается возможным.

У нас есть некие аппаратные ресурсы, которые нужно имитировать. В архитектуре x86 их, в общем-то, всего три:

Регистры процессора (включая регистры служебного назначения). Порты ввода-вывода (использующиеся для обмена информацией с периферией). Оперативная память.

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


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

Но есть несколько неприятных исключений. Вот, к примеру, уже упоминавшийся регистр CR3, управляющий таблицей трансляции оперативной памяти. Собственно, зная «виртуальное» значение CR3, «базовой» операционной системе нетрудно сымитировать собственно таблицу трансляции: достаточно относящиеся к этой таблице области виртуальной памяти пометить при помощи P-флага, получить таким образом перехват всех обращений к этой таблице, и синхронизировать реальную таблицу трансляции с виртуальной, которую гостевая операционная система принимает за реальную (техника «теневых таблиц трансляции», Shadow Page Table). Но при этом, к сожалению, нужно как-то обманывать гостевую операционную систему, «подсовывая» ей «виртуальный CR3» вместо реального, а средств соответствующего аппаратного контроля обычный x86-процессор не предоставляет.

Еще одна проблема из той же серии - внутренний регистр процессора, отвечающий за «уровень привилегий» текущего запущенного приложения. Процессор использует его, чтобы перехватывать попытки обращения «обычных» приложений к «опасным», «недозволенным» инструкциям и областям памяти; назначается этот уровень привилегий операционной системой. Таких уровней всего четыре; о приложениях с заданным уровнем привилегий говорят, что они работают в соответствующем кольце. Чем меньше численное значение данного параметра, тем больше дозволено соответствующим приложениям. В кольце 0 (Ring 0), к примеру, работает операционная система и (обычно) драйвера операционной системы; в кольце 3 (Ring 3) - «обычные» пользовательские приложения. Так вот: доверять «гостевой» операционной системе нулевое кольцо нельзя - иначе невозможно будет перехватывать некоторые её действия, поскольку в нулевом кольце «дозволено всё» и многие проверки безопасности попросту не работают.


Но поскольку гостевая операционная система, естественно, по умолчанию предполагает, что её нужно запускать именно в нулевом кольце, а проверить сей факт особенного труда не представляет, то вполне естественно, что при попытке её запуска в каком-либо другом кольце приложение-виртуализатор добьётся разве что сообщения об ошибке. Поэтому, строго говоря, полноценную имитацию «физического» компьютера с помощью аппаратных ресурсов виртуализации в x86 нельзя. Говорят, что не выполнен критерий самовиртуализируемости Попека и Голберга (Popek and Goldberg self-virtualization requirements).

Как же тогда работают «виртуализаторы» типа VMWare? Довольно нетривиальным образом. Виртуализатор слегка «подрезает крылья» коду выполняющейся под его управлением операционной системы, на лету дизассемблируя её код и заменяя «плохие» инструкции (вроде чтения-записи регистра CR3) нейтральными с её точки зрения (это называется динамической трансляцией; dynamic recompilation). Сделать это, мягко говоря, не так уж просто, а гарантировать работоспособность получающегося на выходе результата - еще сложнее. Приплюсуйте сюда задачку имитации софтом виртуального x86-компьютера (требующую реализации специального сложнейшего драйвера), и вы получите представление о том, почему «виртуализирующее ПО» для x86 до сих пор не отличалось ни особенной надёжностью, ни особенной производительностью. Увы, но в архитектуре IA-32 с её изначально неплохой виртуализационной функциональностью изначально была заложена здоровенная «дырка», которую возможно обойти только с большим трудом.

Интересно, кстати, что в пришедшей на смену IA32 технологии AMD64/Intel EM64T, исправившей большинство неудачных и тонких мест архитектуры, ведущей свою родословную аж с процессора Intel i80386, эту «виртуализационную дырку» ни Intel, ни AMD так и не закрыли! Вместо этого они совершенно независимо друг от друга выпустили две совершенно несовместимые друг с другом «заплатки» к AMD64 и EM64T соответственно, по-разному облегчающие жизнь разработчикам виртуализационного ПО.


Технологии виртуализации вчера, сегодня, завтра


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

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

Повышенный интерес к компьютерным технологиям виртуализации в настоящее время не случаен. Вычислительная мощь нынешних процессоров быстро растет, и вопрос даже не в том, на что эту мощь расходовать, а в том, что современная «мода» на двухъядерные и многоядерные системы, проникшая уже и в персональные компьютеры (ноутбуки и десктопы), как нельзя лучше позволяет реализовать богатейший потенциал идей виртуализации операционных систем и приложений, выводя удобство пользования компьютером на новый качественный уровень. Технологии виртуализации становятся одним из ключевых компонентов (в том числе, и маркетинговых) в самых новых и будущих процессорах Intel и AMD, в операционных системах от Microsoft и ряда других компаний. И в ближайшее время мы можем увидеть на этом поле не менее жаркие баталии, чем те, что недавно гремели по поводу поддержки 64-битных инструкций или двухъядерности в процессорах Athlon и Pentium. Итак, здесь мы сделаем попытку разобраться, в том числе, с технической стороны (не вдаваясь, однако, слишком глубоко в детали), что представляют собой аппаратные технологии виртуализации в процессорах Intel и AMD, а также рассмотрим программные решения по виртуализации от различных производителей, без чего применение виртуализации в компьютерах также немыслимо.

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



Виртуализация послезавтра: виртуальный ввод-вывод


После всех комплиментов в адрес AMD Pacifica может показаться, что ничего принципиально более современного в технологиях виртуализации придумать невозможно. Но на самом деле это далеко не так.

Проведём небольшой мысленный эксперимент. У нас есть один компьютер с каким-то набором аппаратного обеспечения (которое, в сущности, сводится к процессору, оперативной памяти и средствам ввода-вывода). С памятью и процессором мы уже разобрались: они прекрасно виртуализуются, и потому предположим, что на этом «железе» работают сразу несколько операционных систем. А вот кто и как работает из этих операционных систем с «вводом-выводом»? Ну, допустим, разные жёсткие диски и логические разделы этих дисков мы еще как-нибудь разбросаем между разными ОС. А вот возьмём ту же видеокарту: какая из операционных систем с ней должна работать? Не передавать же её «по рукам», перебрасывая от одной ОС к другой, - ведь о присутствии «соседей», «подстраивающих» под себя видеокарту эти ОС могут даже и не подозревать!

Что делать? Единственное разумное решение - применить всё ту же виртуализацию к нашим аппаратным ресурсам. Вместо того чтобы работать с вполне конкретной видеокартой, гостевые ОС работают с некой её «имитацией», которую создаёт модуль VMM, синхронизирующий затем эту имитацию с реальной видеокартой. Но поскольку на действительно сложную имитацию сил программистов обычно не хватает, то и возможности «виртуальной» видеокарточки получаются соответствующие, образца эдак 1996 года в лучшем случае. Правда, менее «навороченные» устройства, к счастью, имитировать куда проще, так что в действительности ситуация далеко не так удручающа, как это может на первый взгляд показаться, однако же своего разрешения она, безусловно, всё-таки требует. А называется это всё «виртуализацией ввода-вывода».

Сейчас, правда, трудно загадывать в будущее: когда мы задавали вопросы сотрудникам Intel, то выяснялось, что соответствующие проекты пока носят статус исследовательских, а уж намерений по созданию и продвижению каких-либо стандартов в этой области у них еще нет и в помине.
Но можно рискнуть предположить, что развитие здесь пойдёт в сторону частичного переноса драйверов устройств из операционных систем в менеджеры виртуальных машин (VMM). Каждый такой драйвер будет предоставлять некий универсальный интерфейс ко всем возможностям видеокарты, учитывающий при этом существование многих «потребителей» этих возможностей; драйвера же уровня операционной системы будут просто предоставлять более высокоуровневый доступ к тем же функциям в терминах специфичной для данной операционной системы графической подсистемы (будь то Windows GDI с DirectX или X Window System с OpenGL). Благо, что на примере AMD Pacifica хорошо видно, что и «место» под драйвера рядом с VMM в системах «второго поколения» замечательно найдётся, и интерфейс между VMM и операционными системами (и даже прикладным ПО) можно сделать чрезвычайно удобным и быстродействующим (возможно даже более быстрым, чем традиционные системные вызовы). Сами же «устройства ввода-вывода» также обзаведутся специфическими аппаратными доработками, упрощающими возможности их одновременного использования несколькими операционными системами одновременно. Вероятно, появится и свой стандарт на VMM и «программный интерфейс» VMM, предоставляемыми ими расширенные возможности для обычных операционных систем. И, вполне возможно, что совсем недалёк тот день, когда на типовом компьютере будет установлен «винегрет» из пары версий Microsoft Windows, Linux, FreeBSD, Solaris, какого-нибудь популярного VMM с открытым кодом, и всё это многообразие будет превосходно, без сегодняшних проблем с драйверами для разных ОС, одновременно в полную силу работать.

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


Виртуализация позавчера: виртуальная память и стандартные интерфейсы операционных систем


Вообще, «виртуализация» - это один из краеугольных камней современной вычислительной техники. По правде сказать, «виртуален» и «невещественен» любой компьютер, начиная еще с первых «пентиумов»: ведь, по сути, любая выполняющаяся на них команда, инструкция, операция в той или иной степени виртуальна. Программы работают с виртуальной, а не физической оперативной памятью, процессоры «на лету» перекодируют x86-инструкции в свой внутренний RISC-подобный формат, драйвера устройств и операционные системы прячут под стандартными интерфейсами доступное в системе оборудование. Это зачастую медленно, это почти всегда сложно, но это - единственный способ хоть как-то гарантировать относительную надежность и сравнительную эффективность той чудовищно, непомерно огромной системы, которую мы называем современным компьютером.

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

У большинства русскоговорящих читателей слово «виртуальный», вопреки его изначальному происхождению, наверное, вызывает примерно одинаковые ассоциации с чем-то невещественным, несуществующим на самом деле. Но изначальный смысл его в вычислительной технике гораздо конкретнее и проще - «виртуальные» объекты здесь всегда означают некие абстрактные интерфейсы, за которыми скрывается реальное оборудование. Основная идея, хорошо прослеживающаяся здесь последние лет двадцать - это стремление максимально упростить задачу разработчикам программного обеспечения, предоставив каждой программе (в идеале) по стандартному «виртуальному компьютеру», на котором она сможет работать без учёта вообще каких бы то ни было сторонних факторов - компьютера, на котором она запущена, или других работающих на этом же компьютере программ.
И, надо сказать, результаты здесь были достигнуты впечатляющие. Первые процессоры работали непосредственно с «физической» оперативной памятью, напрямую указывая в программе конкретную ячейку в модуле памяти, с которой они работали. Получалось что-то вроде «модуль памяти #1, микросхема 4, банк 3, строка данных 63, байт 13, - или, в двоичной нотации, «модуль 01, микросхема 01000, банк 11, строка данных 0111111, байт 01101». Эти числа записывались подряд - и получался адрес 010100011011111101101, то есть 669677 в привычной нам десятичной нотации. При соблюдении минимальных ограничений на организацию модулей памяти при таком способе записи фактически получается, что мы нумеруем ячейки памяти идущими подряд числами, начиная с нуля и заканчивая некоторым большим числом. А это удобно и проектировщикам «железа», и программистам. (Кстати, именно отсюда пошло правило «объем модуля памяти должен являться степенью двойки» - при таком подходе все младшие биты физического адреса модуля получаются допустимыми, и в нумерации адресов физической оперативной памяти не возникает «дырок»). Вот с этими «физическими адресами памяти», образующими отрезок на числовой прямой, первые программы и работали. Система была по своему достаточно изящная, но, к сожалению, совершенно не приспособленная для одновременного исполнения нескольких программ, - в лучшем случае одна программа в компьютере могла на время передавать управление другой.

Вообще, о приёмах работы того времени можно составить неплохое впечатление, если вспомнить, что модули привычной нам динамической оперативной памяти (DRAM), требующие регулярной «подзарядки», программисту в те дни приходилось «обновлять» («регенерировать») самостоятельно. Дело в том, что модули DRAM сравнительно быстро теряют хранящуюся в них в виде заряда микроконденсаторов информацию («быстро» здесь означает «за миллисекунды»), и в то время эту особенность данного типа памяти приходилось учитывать «вручную», то есть программными средствами прописывая обращения к ячейкам («столбцам») и тем самым регулярно обновляя хранящуюся в памяти информацию.


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

Впрочем, сложность, связанная с необходимостью учёта наличия в физической оперативной памяти одновременно нескольких программ (и совершенно разной информации - кода, данных, стека, управляющих структур) - это только полбеды. Главная же беда заключается в том, что в любых программах встречаются различные ошибки (причем, чем сложнее программа, тем этих ошибок больше), очень часто приводящие в первую очередь как раз к порче оперативной памяти по случайному адресу. И «заглючившая» программа, работающая с физической оперативной памятью, в большинстве случаев будет попросту «убивать» не только саму себя (а иногда - и не столько), сколько окружающих её «соседей» по памяти.



Схема 1. Компьютер без виртуализации

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

Подобная система называется сегментированной моделью оперативной памяти, в архитектуре x86 она появилась в процессорах i80286 (где получила название «защищенного режима») и исчезла из этой архитектуры только с переходом к 64-битным наборам инструкций AMD64/Intel EM64T.





Схема 2. Сегментированная память

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

Эффективная схема? Вполне! Она работает, она удобна, доступна и понятна, так что многие любители ассемблера до сих пор с удовольствием ею пользуются. Но долго она не продержалась, поскольку, как легко догадаться, особенной гибкостью в использовании не отличается. Как мы изначально «нарежем» память ломтиками, - так оно в будущем и останется: выделим слишком много - какие-то области останутся неиспользованными и простаивающими; выделим слишком мало - не сможем в нужный момент увеличить этот объём. Помнят ли еще программисты старые добрые DOS-овские среды разработки от Borland, где в опциях компилятора указывалась «модель памяти», в которой определялся размер и количество используемых в программе сегментов? И помнят ли пользователи замечательную утилитку mem и знаменитое Not enough memory, которыми так радовали глаз пользователя ранние «персоналки»?

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


Об удачности этой разработки говорит хотя бы то, что вплоть до перехода к 64-битным наборам инструкций «ядро» любых x86 в точности соответствовало стандарту IA-32 (Intel Architecture for 32-bit), введённому Intel для i386 (так что, в принципе, на «трёшках» должны работать любые 32-битные программы, не задействующие слишком современных функций).

Виртуальная память() - это логическое развитие идеи сегментированной памяти, когда мы переходим от вполне конкретным образом преобразуемых в физические «линейных» адресов защищенного режима x86 к совершенно абстрактным «виртуальным» адресам. Ведь, по большому счёту, для работающей на компьютере программы совершенно безразлично, что за «физические» ячейки памяти она использует! Ей нужен просто некоторый диапазон адресов, по которым она сможет сохранять свои данные, а что за этими «цифирками» на самом деле скрывается, ей глубоко безразлично. Главное - чтобы процессор знал, как эти абстрактные цифры (виртуальные адреса) переводить во вполне конкретные инструкции для контроллера памяти (физические адреса).

Как это делается на практике? Вся доступная процессору физическая оперативная память разбивается на небольшие кусочки размером 4 Кбайт или 4 Мбайт - «страницы». При этом используется та же схема, что и при разбивке физических адресов на адреса конкретной ячейки памяти: младшие 12 или 22 бит виртуального адреса обозначают смещение данного адреса от начала страницы, а старшие биты (от 10 до 50) - номер страницы. Когда процессору требуется вычислить физический адрес по виртуальному, он просто разделяет виртуальный адрес на номер страницы и смещение, заглядывает в таблицу, где для каждого номера указаны координаты начала страницы в физической памяти, и прибавляет к полученной координате смещение(). Упомянутая табличка страниц называется таблицей трансляции адресов виртуальной памяти (или просто таблицей трансляции), и размещается она в виде B-дерева в самой обыкновенной оперативной памяти, что позволяет создавать без большой избыточности сколь угодно большие быстродействующие таблицы трансляции.


Работает это дерево, правда (как и всё, связанное с оперативной памятью), по-прежнему не очень быстро, и поэтому процессор кэширует ранее определенные соответствия «номер страницы - запись в таблице трансляции» в специальном кэше - буфере трансляции виртуальных адресов (Translation Look-aside Buffer, TLB).

Детали таблицы трансляции

Фраза про B-дерево может прозвучать устрашающе, но на самом деле скрывается за этим не такая уж и сложная технология. Двоичный номер виртуальной памяти, по всё той же доброй традиции, «разрезается» на несколько кусочков небольшого размера (по 10 бит): к примеру, 00000000001111111111010101010101 - превращается в 0000000000 + 1111111111 + 010101010101. Первая часть адреса - 0 - это «номер директории», вторая - 1023 - «номер страницы», третья - 1365 - смещение от начала страницы.

Что дальше с этим всем делается? В процессоре есть специальный регистр под названием CR3 (Control Register #3), в котором записывается «указатель на таблицу трансляции» - физический адрес, по которому в памяти располагается «таблица директорий». Эта самая таблица - это 1024 записи длиной по 32 (или 64) бита, в которых записаны физические адреса «таблиц страниц», соответствующих той или иной директории. У нас директория номер ноль, а потому процессор, декодирующий виртуальный адрес, вычисляет сумму регистра CR3 с нулём и получает адрес нужной ему «таблицы страниц». Вот в этой таблице (тоже из 1024 записей длиной 32 или 64 бита) уже записаны физические адреса начал страниц, так что, прибавив к началу таблицы страниц номер страницы (1023) - мы выходим на запись, в которой находится физический адрес начала нужной нам страницы. Остаётся только прибавить к нему 1365 - смещение - и искомый физический адрес готов. В случае 64-битной организации памяти уровней трансляции в этой схеме не два, а четыре; в случае трансляции со страницами размером 4 Мбайт - последний уровень трансляции пропускается.



Зачем вообще понадобилась столь сложная схема и почему было нельзя ограничиться одной таблицей трансляции? Всё дело в размере таблиц. Для 32-битной адресации памяти и страниц размером 4 Мбайт необходимый размер таблицы составляет всего лишь 4 или 8 Кбайт памяти, но для более востребованных 4 Кбайт - страниц, и, еще хуже, для 64-битной адресации памяти, необходимые размеры таблицы получаются гораздо большими - от 4-8 Мбайт до 8 Гбайт и даже 8 Тбайт. Во времена 386-х процессоров даже 4 Мбайт для таблицы трансляции адресов одной программы казалось слишком большой цифрой (а, учитывая, что на компьютере могут быть одновременно запущены сотни программ, и для каждой потребуется минимум по 4 Мбайт физической памяти - это и для современных систем слишком много); и потому выбор был сделан в пользу двухуровневой трансляции, при которой трансляцию можно остановить еще на «верхнем» уровне, указав для некоторых записей в «таблице директорий», что они не соответствуют никаким реальным физическим адресам и убрав, таким образом, необходимость в указании для целого диапазона адресов записей в таблице трансляции.

Кстати, сегментация (в 32-битных процессорах) даже с виртуальной памятью всё равно сохраняется. То есть реальные адреса, упоминающиеся в программе, вначале превращаются с учётом сегментов в «линейные», а уже они с помощью таблицы трансляции - в реальные «физические» адреса.

Трудно поверить, но, казалось бы ничем глубоко принципиальным не отличающаяся от обычной сегментированной модели памяти, память виртуальная даёт системному программисту практически всё, чего только его душа пожелает. Дело в том, что собственно усовершенствованной «трансляцией» адресов (которая сама по себе снимает все проблемы сегментированной оперативной памяти) виртуальная память не ограничивается. Вся «соль» технологии - в том, что для каждой записи в таблице трансляции адресов (фактически - для каждого диапазона адресов виртуальной памяти) определен набор специальных флагов, которые реализуют:



Защиту важных участков оперативной памяти от перезаписи.
Один из простейших «флажков», который указывается для адресов виртуальной памяти - это флажок «только для чтения», позволяющий защитить определенные области виртуальной оперативной памяти от записи. К примеру, обычно read-only объявляются страницы, содержащие машинный код программы.

Защиту программ от вирусов.

Основа новомодных «антивирусных» технологий вроде Microsoft Data Execution Prevention, обеспечивающих надёжную защиту компьютера от эксплойтов, использующих атаки типа «переполнение буфера», - крошечный битик в таблице трансляции (No eXecute - NX у AMD, и eXecute Disable, XD - у Intel), запрещающий выполнение машинного кода из определенных участков памяти.

Защиту операционной системы.

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

Эффективный менеджмент оперативной памяти.

Целый ряд специальных битов позволяет операционной системе отслеживать, по каким адресам программа читала или записывала данные, и определить «глобальные» страницы памяти, общие для всех программ в процессоре.

Но самый главный бит в таблице трансляции - это «нулевой» бит P - Present, обеспечивающий собственно

По-настоящему виртуальную память.

На самом деле, назначение этого бита довольно простое - если он «сброшен» (установлен в 0), то любое обращение к оперативной памяти по этому адресу вызывает системную ошибку (исключение), называющуюся Page Fault (#PF). Но сколько же на основе этой «простоты» удаётся построить! Ведь, по сути дела, P-флаг, - это указание процессору, что для обработки обращения программы к данному адресу памяти требуется обратиться за помощью к операционной системе.

Судите сами: простейшее применение P-флага - это реализация своп-файла, позволяющего использовать жёсткий диск вместо физической оперативной памяти.


Идея в том, что мы можем для некоторых страниц виртуальной памяти не ставить им в соответствие никакого физического адреса оперативной памяти, а «сбросить» для соответствующих записей в таблице трансляции P-флаг и сохранить страницу в файл на жёстком диске. Если обращений к данной странице не происходит - то всё хорошо. Если происходит - то генерируется исключение #PF. По сути своей процессор просто приостанавливает выполнение текущей программы, и заглядывает в свой «справочник по действиям в нештатных ситуациях» - специальный участок памяти, в котором прописано, какую подпрограмму операционной системы вызывать в том или ином случае. В полном соответствии со стандартом, процессор вызывает обработчик исключения #PF - один из ключевых фрагментов любой операционной системы. Обработчик (фактически - операционная система) - «смотрит» на возникшую ситуацию («программа такая-то полезла в память по адресу такому-то и была остановлена потому что флаг Present был сброшен»), определяет, что данному адресу соответствует страница памяти, которой нет в оперативной памяти, но которая есть на жёстком диске - и начинает действовать:

Он выбирает из физической памяти еще никем не занятую страницу, или даже освобождает одну из страниц, сохраняя её данные на диск, и сбрасывая соответствующий P-флаг в таблице трансляции. Читает нужное место своп-файла, копируя данные из неё в эту страницу. «Модернизирует» таблицу трансляции, прописывая в ней новый физический адрес для страницы, из-за которой случился сбой. Обновляет при необходимости данные в буфере TLB.

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



Другие «популярные» применения P- флага виртуальной памяти позволяют реализовывать, к примеру технику «мэппинга файлов на память». «Мэппинг» - это когда программа по технологии, аналогичной технике свопинга, отображает по запросу приложения тот или иной файл в пространство виртуальных адресов программы. То есть можно добиться того, чтобы попытка чтения ячейки памяти #13323658 выливалась бы в автоматическое чтение какого-нибудь файла program.data с позиции 3446. Это, во-первых, весьма удобно (файл не нужно «читать» или «писать» - он уже доступен программе в виде обычного массива или набора массивов), во-вторых, очень быстро (производится лишь минимально необходимый набор действий по загрузке или записи данных), а в-третьих, очень эффективно (файл автоматически «кэшируется» в оперативной памяти, неиспользуемые страницы из этого кэша автоматически же убираются, освобождая физическую память, при сохранении сохраняются только действительно изменившиеся фрагменты файла, а не всё подряд, и т.п.). Хотя из-за ограничений сравнительно узкого доступного программе, работающей под управлением 32-битных версий Microsoft Windows, виртуального пространства в 2-3 Гбайт, и весьма громоздкой и неудобной реализации данной техники средствами Win32 API, используется она достаточно редко.

Более сложный пример, задействующий виртуальную память: реализация систем с якобы общей памятью в системах, где эта память изначально раздельна. Например, в разных компьютерах, соединенных при помощи локальной сети (в общем случае - в кластерах). Идея подобных систем состоит в том, чтобы при обращении программы по виртуальному адресу, соответствующему «чужой» памяти, вызывать обработчик, который сгенерирует обращение по сети к «чужой» машине, получив которое, эта машина выполнит нужную операцию с памятью и вернёт первоначальной машине ответ, который отобразится в программе. В результате можно добиться такого эффекта, что у нескольких физически совершенно разных компьютеров для программ виртуальная память будет пересекаться, или вообще полностью совпадать.



Это и есть суть «виртуальной памяти» - пользовательская программа никогда не может с уверенностью утверждать, что скрывается за абстрактным виртуальным адресом. Мы можем как угодно «дурачить» её, осуществляя за её спиной подтасовки с реальными адресами, и добиваться с этой помощью самых разнообразных эффектов. Впрочем, об этом мы поговорим в следующем разделе. А пока - перечислим основные и более востребованные преимущества виртуальной памяти:

Виртуальная память обладает всеми преимуществами сегментированной. Но при этом размеры виртуальной памяти, выделенной программе, могут сколь угодно гибко изменяться. Виртуальная память может «физически» размещаться не только в оперативной памяти, но и на жёстком диске и даже в Сети. Виртуальная память не обязана быть непрерывной - её можно «нарезать» вообще как угодно, лишь бы это нам было удобно. Можно задавать произвольное число «пересекающихся» областей виртуальной памяти для разных программ, вплоть до того, что одни и те же данные будут многократно отображены в адресное пространство программы по разным адресам. Виртуальная память обеспечивает очень гибкую многоуровневую защиту оперативной памяти, позволяющую отлавливать любые ошибочные действия программы. И не только ошибочные: в задачах отладки приложений виртуальная память позволяет, к примеру, отследить в любой момент такое «отладочное событие», как простое чтение программой того или иного адреса в памяти.

Минусов у виртуальной памяти всего два. Во-первых, она существенно замедляет работу компьютера (даже простая трансляция виртуальных адресов, не попавших в TLB - очень неторопливое занятие; обработка же события #PF - и вовсе способна занять сотни тысяч тактов процессора); а во-вторых, - сложна и абсолютно непрозрачна для рядового программиста.


Виртуализация сегодня и завтра: Intel VT и AMD «Pacifica»


Корпорация Intel пошла достаточно прямолинейным путём, попросту выпустив «минимально необходимую» заплатку к x86. Полное название «заплатки» - Intel Virtualization Techology for x86 (VT-x); одновременно была выпущена аналогичная виртуализационная «технология» для процессоров Intel Itanium (VT-i). Впрочем, рассматривать последнюю технологию мы не будем, поскольку по сути своей она практически полностью аналогична VT-x. Напомним, что ранее данная технология была известна под кодовыми именами Vanderpool (для персональных компьютеров) и Silvervale (для серверов).

Что же сделала Intel? Довольно нетривиальную, хотя и напрашивающуюся вещь. Разработчик архитектуры IA-32 попросту ввела в своих процессорах специальный «режим выполнения виртуальной машины» (Virtual Machine eXecution mode, VMX), предназначенный специально для виртуализационного ПО (Virtual Machine Manager, VMM), и определила для этого режима несколько ключевых «виртуализационных» инструкций, таких как, к примеру, «создать виртуальный компьютер» и «запустить виртуальный компьютер». Собственно этот самый «виртуальный компьютер» в VT-x описывается специальной структурой под названием VMCS (Virtual Machine Control Structure) и по сути своей является небольшим участком физической оперативной памяти, хранящим минимально необходимые данные для запуска гостевой операционной системы, а также данные, необходимые для безопасного выхода из режима работы гостевой ОС, и некоторые настройки, относящиеся к управлению этой виртуальной машиной.

Программист практически вручную создаёт эту структуру, полностью описывающую необходимый ему виртуальный компьютер и его свойства, после чего «загружает» её в специальный аппаратный регистр «текущей виртуальной машины». Затем программист может «запустить» свежесозданную виртуальную машину специальной инструкцией.

Запущенная виртуальная машина работает на обычных аппаратных ресурсах компьютера (указанных VMM в описании виртуальной машины) и для запущенного на ней программного обеспечения практически ничем не отличается от обычной «физической» машины.
Но в отличие от «обычного» режима, в эту виртуальную машину можно вставлять сколь угодно много «закладок», которые будут прерывать её исполнение, передавая управление обратно к VMM, который будет вручную имитировать то или иное событие, или выполнение той или иной инструкции. Какие именно события будут перебрасываться для «ручной обработки» VMM, определяется только самим программистом, но их в любом случае даже больше, чем необходимо для реализации сколь угодно сложной виртуальной машины, вплоть до точной имитации совершенно другого процессора. К примеру, проблемная в обычном случае операция чтения-записи регистра CR3 в VT-x попросту приведёт к тому, что процессор на секундочку приостановит гостевую ОС, вызовет VMM, который программно сымитирует результат её выполнения (не выполняя настоящую инструкцию MOV to/from CR3 с аппаратным регистром, а подменив её чтением-записью обычного кусочка оперативной памяти), после чего возобновит выполнение гостевой ОС, которая «подвоха» даже не ощутит.

При желании можно даже вручную «подкручивать» счётчик тактов процессора и показания системного таймера, чтобы у гостевой ОС не возникло ненужных сомнений, по поводу того, с чего это вдруг некоторые инструкции на данном «процессоре» выполняются так долго. Можно даже попробовать один-в-один сымитировать AMD Athlon XP на Intel Pentium 4 - так, что программное обеспечение «гостевой» операционной системы не будет и догадываться о подмене. Правда, если быть совсем точным, то поскольку Pentium 4 не поддерживает набор инструкций AMD 3Dnow!, а технология VT-x не позволяет перехватывать ошибки типа «неизвестная инструкция» (Invalid Opcode), то сымитировать поддержку 3Dnow! на P4 невозможно. Но 3Dnow! всё равно сегодня практически не используется, а во всём остальном (скажем, во всём том, что рапортует о процессоре стандартная инструкция CPUID), имитируемый компьютер будет вести себя точно как AMD Athlon XP, так что подавляющее большинство ПО на «уловку» поддастся.



Вот и вся технология виртуализации VT-x - мы просто переводим наш процессор в такой режим, когда он перехватывает некоторые (определенные нами) события и передаёт их в специальную программу - менеджер виртуальной машины. И никакой сложной бинарной трансляции!

Всего в VT-x десять новых инструкций:

VMXON и VMXOFF включают и выключают режим VMX. VMWRITE позволяет программисту записывать данные в структуру VMCS, описывающую виртуальную машину; VMREAD - аналогично читать данные из VMCS. Собственно формат структуры VMCS официально неизвестен, и каким образом и что там, вообще говоря, сохраняется - одна Intel знает. Заметим, также, что сама структура VMCS относительно невелика по размерам (единицы килобайт) и не хранит в себе, к примеру, данных о виртуальной памяти, образующей физическую память «виртуального компьютера», - эти данные менеджер VMM поддерживает (загружает и сохраняет) для виртуальных машин самостоятельно. VMPTRLD позволяет выбрать текущую виртуальную машину (указатель на VMCS). VMPTRST, аналогично, сохранить указатель на текущую виртуальную машину. VMLAUNCH позволяет запустить выбранную виртуальную машину (описывающуюся ранее установленным указателем на корректную текущую VMCS). Исполнение кода работающей виртуальной машины прерывает либо наступление указанного в VMCS события (внешнего прерывания, попытки выполнить ту или иную инструкцию), либо выполнение инструкции VMCALL (если она разрешена в настройках VMCS). VMRESUME позволяет продолжить прерванное событием выполнение кода на виртуальной машине. VMCLEAR используется для инициализации пустой структуры VMCS и для перевода выбранной виртуальной машины в «остановленное» состояние (с сохранением данных VMCS).

Доступ к инструкциям VT-x по умолчанию заблокирован; для их включения требуется «включить» бит 4 в четвёртом контрольном регистре процессора (CR4.VMXE=1) и «включить» биты 0 и 2 в MSR-регистре 3Ah. На виртуальной машине, эмулируемой с помощью VT-x, можно замаскировать поддержку VT-x, сообщаемую инструкцией CPUID, и принудительно заблокировать любую возможность использования в виртуальной машине инструкций данного семейства.


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

Технология Intel VT уже вышла на рынок - в продаже есть как настольные, так и серверные процессоры, официально ее поддерживающие. Перечень процессоров Intel с поддержкой VT постоянно пополняется, и вы без труда сможете найти его на сайте Intel вместе с популярным описанием самой технологии. Корпорация для популяризации этого своего решения любит устраивать живые демонстрации возможностей технологии виртуализации, когда сперва «типичный профессионал» сидит рядом с четырьмя системными блоками (выкрашенными для наглядности в разный цвет), один из которых считает профессиональную задачу, на другой выполняется офисная работа, третий «крутит» небольшой сервер, четвертый занимается обслуживанием и антивирусной проверкой (все - под разными ОС). И каждую из этих систем можно независимо «подвесить», «заразить» или перезагрузить, в то время как остальные продолжают свою деятельность. И кульминацией этой демонстрации становится момент, когда открываются крышки этих системных блоков, а под ними - пусто. Рядом же стоит обычный «неприметный» системный блок (для наглядности выкрашенный в эти же 4 цвета - в полосочку), который один и выполняет всю описанную выше работу - под управлением Intel VT с аппаратной поддержкой и несколькими одновременно работающими ОС. На неискушенных пользователей такая демонстрация оказывает большое впечатление.



Технология Intel VT в действии



Вместо нескольких системных блоков с различными ОС работает один

Другой, более «бытовой» демонстраций Intel VT обычно выступает домашний мультимедийный компьютер, который одновременно обслуживает несколько независимых пользователей: например, папа работает с почтой, мама смотрит фильм, а дитё - да, играет или слушает музыку.


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

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


Виртуализация завтра: AMD Secure Virtual Machine «Pacifica»


С AMD ситуация еще интереснее: в 2005 году эта компания сильно удивила мировую общественность, выпустив свою технологию виртуализации, абсолютно несовместимую с технологией, предложенной Intel. Вот так вот! Раньше процессоры Intel и AMD работали на одних и тех же материнских платах, затем стали работать на разных, затем потребовали разных типов оперативной памяти, а теперь вот - и разного программного обеспечения. Можете даже не пытаться установить виртуализационный софт «для Intel» на процессоры AMD - там нет ничего даже близко похожего.

Правда, с другой стороны, решение AMD совершеннее и современнее. А несовместимость подходов проявляется лишь в относительно небольшой части кода модуля VMM (любые операционные системы и обычное ПО, естественно, будут по-прежнему одинаково хорошо работать на процессорах и Intel, и AMD). Но - тем не менее.

Давайте на минутку вернёмся к Intel и посмотрим на то, какой подход был положен в основу идеологии VT-x. У нас есть некая «базовая» операционная система, в которой пользователь запускает приложение типа VMWare Workstation или Microsoft VirtualPC, а уж это приложение при желании может подкрутить что-то в процессоре, включить режим виртуализации, создать специальный модуль по управлению виртуальными машинами, создать виртуальные машины, и запустить на них какие-то «гостевые» операционные системы со своими приложениями. При этом «режим VMX» не является чем-то сильно особенным и запущенный в этом режиме код никакими особенными привилегиями не обладает. Фактически это просто обычное приложение (или драйвер), запущенное на процессоре и обладающее лишней парой флажков, записанных глубоко в служебных регистрах процессора.

У AMD подход принципиально другой, более простой и наглядный. Начинается он тоже с менеджера виртуальных машин VMM, да только вот менеджер тот на интеловский аналог абсолютно не походит. У Intel VMM - это некое очень хитрое приложение обычной операционной системы, которое может быть запущено даже из кольца обычного приоритета.
У AMD - это системный код, работающий на более низком уровне, чем сама операционная система, и запускаемый исключительно из системного Ring 0. Модуль VMM в Pacifica фактически выполняет роль ядра некоторой «базовой» операционной системы, и только этот код в Pacifica работает с собственно физическим оборудованием. Все «обычные» операционные системы в подходе AMD - гостевые и работающие с виртуальными машинами, которые для них создаёт VMM. Оцените всю прелесть решения: у Intel модуль VMM в поте лица занимается «фальсификацией» кучи разных событий, выбиваясь из сил, лишь бы сделать вид, что гостевая ОС работает с реальным «железом». У AMD модуль VMM попросту один-единственный раз создаёт виртуальную машину, переключается в «гостевой режим» - и запущенный в этой гостевой машине код работает со свежесозданной виртуальной машиной в практически полностью автономном режиме, без какого-либо вмешательства VMM.



Схема 9. Виртуализация с AMD Pacifica

Как такое возможно? Да очень просто: AMD действительно виртуализирует физические ресурсы, и, прежде всего, - оперативную память. «Физическая» виртуальная память, видимая гостевыми ОС в Pacifica - это просто виртуальная память второго уровня. Таблицы трансляции для которого контролирует, естественно, VMM. То есть если какая-нибудь программа, запущенная в этой гостевой ОС, скажем, обращается к памяти по адресу такому-то, то процессор, используя таблицы трансляции, контролируемые гостевой ОС, вначале преобразует этот адрес в «физический» адрес, который уже аппаратно, без участия VMM преобразуется в настоящий физический адрес. Или, как уже говорилось, не преобразуется, а вызывает подкачку из своп-файла, загрузку данных по сети, и прочее - возможности виртуальной памяти в умелых руках безграничны. VMM, кстати говоря, устроен гораздо проще операционной системы, полностью независим от неё, и потому потенциально возможно реализовывать на его основе всю ту «редкую» функциональность, которую обычно не рискуют (или не хотят) вносить в ядра обычных операционных систем.



Еще одна интересная особенность Pacifica - это специальный защищенный режим запуска VMM. При желании на компьютере с процессором, поддерживающим данную технологию, можно сделать так, чтобы при старте компьютер на аппаратном уровне проверил цифровую подпись к VMM. Проще говоря, можно «зашить» в компьютер такую программу, что на нём принципиально невозможно будет запустить неподписанные доверенным источником модули VMM. А сами модули от этого «доверенного источника», в свою очередь могут проверить цифровую подпись у запускаемых ими операционных систем; операционные системы - проверять цифровую подпись у запускаемых приложений, и так далее, до сколь угодно высоких степеней контроля того, что именно запущено на компьютере.

Из просто-таки напрашивающихся примеров - можно, к примеру, будет создать такой компьютер, на котором будут запускаться только лицензионные операционные системы производства Microsoft без единого байта «чужих» изменений со стороны любителей халявного ПО. Вернее сказать, запускать на этом компьютере, при желании (скажем, после перепрошивки BIOS) можно будет, конечно, всё что угодно, но при этом в процессоре не взведётся специальный «бит безопасности», легко проверяемый абсолютно любым приложением, и с подобным «не прошедшим проверку» компьютером, к примеру, может отказаться работать какое-нибудь ПО. Для обычного пользователя, боюсь, подобная возможность выльется в очередную кучу проблем с копирайтом и свободным софтом, однако для корпоративных пользователей поддержка «безопасного режима» может оказаться жизненно важной, поскольку позволяет гарантировать безопасность среды, в которой запускается корпоративное приложение. Проверил «безопасный флаг» - значит компьютер гарантированно «чистый», «проверенный». Нет этого флага - значит, в системе что-то изменилось: поменяли часть «железа» (поменяли клавиатуру, на содержащую «жучок»; добавили шпионскую PCI-плату, собирающую информацию о ПО; поставили чужую сетевую карту; посадили в систему «руткит» и так далее).


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

Впрочем, справедливости ради, нужно отметить, что у Intel разрабатывается (уже в течение нескольких лет) собственная технология аппаратной безопасности под названием LaGrande, которая умеет очень многое и даже более готова к выходу на рынок, чем Pacifica. Так что, вполне возможно, что связка Intel VT+LaGrande окажется не менее функционально привлекательной, чем AMD Pacifica. А уж в умении Intel устроить грамотный и широкомасштабный маркетинг своим продуктам сомневаться не приходится.

Само по себе функционирование VMM у AMD очень напоминает функционирование VMM в технологии Intel: VMM с помощью специальных инструкций подготавливает специальные управляющие структуры, описывающие виртуальные компьютеры, «запускает» эти структуры на исполнение и перехватывает выбранные события, там происходящие, подменяя их «ручной» работой. Главное отличие - это объем и тип выполняемой работы. У AMD нет необходимости заниматься сложнейшим менеджментом памяти с подставными таблицами трансляции и синхронизацией этих таблиц с настоящими; нет необходимости в перехвате связанных с этими таблицами трансляции событий. Нужно перехватывать и обрабатывать только некоторые внешние события (скажем, прерывание от таймера, чтобы переключаясь между гостевыми операционными системами создавать иллюзию их одновременной работы; или прерывание от клавиатуры - переключаться между гостевыми ОС по нажатию определенной комбинации клавиш). Да и то, при желании в Pacifica можно попросту «напрямую» подключить выбранные устройства в гостевых операционных системах к физическим ресурсам (в VT-x, для сравнения, любое обращение гостевой ОС к портам ввода-вывода принудительно перехватывается модулем VMM).



Есть и другие особенности, позволяющие минимизировать количество ненужных переключений от гостевой ОС к VMM и обратно, переложив соответствующие проверки на аппаратное обеспечение. А если и приходится всё же переключаться между гостевой ОС, VMM, и другой гостевой ОС - то, опять же по контрасту с VT-x, это переключение происходит крайне быстро, с очень ограниченной необходимостью в сохранении контекста и полным отсутствием необходимости сброса, к примеру, буфера TLB, который обычно при переключении между разными таблицами трансляции приходится полностью очищать. Естественно, что набор перехватываемых событий и возможности «фальсификации» всего и вся ничуть у Pacifica не уже, чем у VT-x; однако настройка что перехватывать, а что нет - гораздо гибче (в VT-x многое перехватывается безусловно; в Pacifica можно отключить практически любой перехват). Даже не знаем, к чему придраться, - вряд ли можно было придумать нечто большее по функциональности, удобное в использовании, и столь быстродействующее.

Набор инструкций Pacifica столь же прост и изящен, как и само решение AMD:

Команда VMRUN переключает выполнение на выбранную виртуальную машину. Из виртуальной машины управление возвращается либо по перехвату одного из указанного в настройках виртуальной машины событий, либо при вызове специальной инструкции VMMCALL (если последняя разрешена настройками). Информация о виртуальной машине хранится в специальной структуре данных VMCB (Virtual Machine Control Block) известного формата. VMM работает с данной структурой напрямую, вручную изменяя при необходимости соответствующие поля (в отличие от VT-x, где формат аналогичной структуры VMCS официально неизвестен и для работы с VMCS используются специальные инструкции VMREAD и VMWRITE). На всякий случай еще раз напомним, что сама эта структура относительно невелика и описывает только состояние процессора виртуальной машины, а виртуальную память и состояние виртуальных устройств этой машины VMM должна обслуживать самостоятельно. Самые необходимые операции по переключению контекста при переходах VMM к гостевой ОС и обратно выполняются полностью автоматически.


Однако, чтобы не совершать лишних действий, сохраняется и загружается действительно только самое необходимое, и при необходимости каких-либо сложных действий или переключения на другую гостевую ОС, «дополнительные» операции сохранения состояния процессора в VMCB и обратной загрузки выполняются инструкциями VMLOAD и VMSAVE. Инструкция SKINIT позволяет начать загрузку процессора в «безопасном режиме», на аппаратном уровне гарантировав соответствие загрузчика (до 64 Кбайт кода) указанной в аппаратуре (в модуле TPM) цифровой подписи

Пять инструкций. Против десяти в куда более сложном и менее функциональном VT-x. Ну как еще выразить восхищение архитекторами наборов инструкций AMD? Правда, к «пяти базовым» для полного раскрытия возможностей Pacifica можно еще использовать «тактические» три инструкции, позволяющие дополнительно ускорить скорость работы Pacifica: STGI, CLGI - управляют схемой перехвата прерываний в Pacifica (включают-выключают «глобальный перехват прерываний»). INVLPGA - сбрасывает TLB, но не целиком, а только те записи TLB, которые относятся к конкретной гостевой ОС (или к VMM).

Как и в случае с VT-x, для того, чтобы получить доступ к новым инструкциям, программному обеспечению нужно эти инструкции разблокировать (установить 12-й бит регистра EFER MSR, отныне известный как EFER.SVME). При желании в Pacifica можно отключить все её продвинутые функции, вплоть до отключения двойной трансляции виртуальных адресов, что позволяет максимально приблизить (хотя и не до конца) схемы использования к VT-x.

В целом решение AMD явно охватывает всю мыслимую область применимости решения Intel, но изящнее, быстрее и проще в использовании; а главное - обеспечивает более чем достаточный запас прочности для того, чтобы считаться полноценным виртуализационным решением будущего. Тем более что соответствующие процессоры должны появиться уже совсем скоро - в первом квартале 2006 года.

Интересно, что на последнем московском Intel Developer Forum (прошедшем в октябре 2005 года) в докладах совершенно неожиданно прозвучали всё те же знакомые «двойные таблицы трансляции», «защита DMA» и прочие характерные функции Pacifica, рекламировавшиеся как...


второе поколение систем виртуализации Intel. К первому, естественно, относилась «закрывающая дыры x86» технология VT-x. Честно говоря, сам московский IDF с нашей субъективной точки зрения, оказался в плане информации по виртуализации крайне скуп, а уровень «познаний» заменявших своих иностранных коллег сотрудников, выступавших с докладами порой просто шокировал - они были не в состоянии отвечать на задаваемые им из зала неспециалистами вопросы! Но позднее замечательнейший человек - Всеволод Предтеченский, в одиночку заменяющий всех остальных технических специалистов российского отделения Intel - в личной беседе пояснил, что этим самым «вторым поколением» должна стать давным-давно анонсированная «технология безопасности» LaGrande (а вернее, то, во что этот проект превратился к настоящему времени), которая вберёт в себя не только новейшие технологии виртуализации (о которых мы поговорим в последней части), но и сложные системы обеспечения гарантированной безопасности (вплоть до шифрования передаваемых по USB данных мышкой).


VMWare Workstation и VMWare Server


В России имя VMWare является практически синонимичным для «программного обеспечения для виртуализации». Именно эта компания в 1999 году впервые вывела на рынок успешный продукт, обеспечивавший для операционных систем производства Microsoft возможность запуска виртуальных машин с «чужими» операционными системами. Правда, в 2003 году VMWare была скуплена корпорацией EMC2, в состав которой с тех пор и входит, однако своего существования в качестве самостоятельного игрока с раскрученным брендом она с тех пор не прекратила. И текущая политика руководства EMC2 заключается в том, чтобы VMWare и дальше работала на рынке как самостоятельная единица, влиять на стратегию и тактику которого EMC особо не будет.

На сегодняшний день VMWare предлагает три линейки базового и некоторое количество сопутствующего виртуализационного ПО(табли- ца 1). Первая линейка, VMWare Workstation 5.5 ориентирована прежде всего на обычных разработчиков, запускающих на своём компьютере несколько операционных систем одновременно. Вторая, VMWare Server GSX 3 - практически идентична первой по основной функциональности, но ориентирована уже на серверное применение в качестве средства организации множества защищённых виртуальных серверов на одном физическом. Существуют версии обоих пакетов для Windows 2000/XP/2003 и основных дистрибутивов Linux. Третья линейка, VMWare Server ESX 2 стоит несколько особняком, поскольку ориентирована не на запуск в качестве обыкновенного приложения в «родительской» операционной системе, а, фактически, реализует свою собственную операционную систему, в которой запускается одно-единственное приложение - собственно виртуализационное ПО. Область применения Server ESX примерно та же, что и у Server GSX, но ESX ориентирована на большие дата-центры, требующие особой надёжности от виртуализирующего ПО.

Конфигурация виртуальных машин у VMWare более чем достойная. Ресурсы процессора доступны виртуальной машине в полном объеме (если на «родительской» машине стоит Pentium 4 - в имитируемом компьютере будет стоять точно такой же процессор); объем оперативной памяти - практически неограничен (до 3,6 Гбайт на каждую виртуальную машину); подключаются напрямую или имитируются стандартные IDE-устройства (жёсткие диски и оптические накопители в виде файлов на диске), поддерживается прямое подключение SCSI-адаптеров и имитация SCSI-дисков, подключенных через контроллер LSI Logic Ultra160 или Mylex BT-958.
Видеокарта - абстрактный графический адаптер VGA/SVGA. Поддерживается и эмулируется до двух флоппи-дисков, до четырех COM-портов, UCHI-контроллер на 2 порта USB 1.1; до двух параллельных LPT-портов, стандартная 104-кнопочная клавиатура и мышь PS/2. Поддерживается до четырех виртуальных сетевых карт (AMD PCnet) и даже виртуальная локальная сеть, состоящая из произвольного числа хостов и до девяти виртуальных свитчей. В общем, звание лидера VMWare удерживает вполне заслуженно.

VMWare использует в своих продуктах классическую технологию «бинарной трансляции»; в последние версии ПО включена и экспериментальная поддержка технологии виртуализации Intel VT-x. Поддержка технологии виртуализации AMD «Pacifica» обещана в самом ближайшем будущем. К слову, именно продукты VMWare корпорация Intel использовала еще год назад для публичных демонстраций (например, в рамках Intel Developer Forum) будущих возможностей своих процессоров с Virtualization Technology, тогда еще не оснащенных блоками VT. И, надо заметить, что, например, на трехгигагерцовом процессоре Intel Xeon (ядро Nocona) работа такой виртуальной системы не отличалась особой прытью, в чем нам довелось убедиться лично.

В перечислены официально поддерживаемые VMWare операционные системы (другие ОС также могут работать).