Шрифт:
Центральная идея Intel Virtualization Technology (ранее известной под названием Vanderpool) - введение в процессор аппаратной поддержки некой специализированной программы - менеджера виртуальной машины (VMM, Virtual Machine Manager) (рис. 1). В принципе, VMM - по всем статьям обычная программа, работающая на персональном компьютере. Однако «права» у этой «обычной» программы самые невероятные, поскольку она может вмешиваться во все мало-мальски значимые события, происходящие в процессоре. То есть, если, скажем, ядро операционной системы (работающее в обычном, ничем не ограничиваемом Ring 0) вызывает инструкцию CPUID, или читает-записывает важный системный регистр CR[Что, впрочем, неудивительно - AMD сегодня отвоевывает себе «место под рыночным солнцем», а для этого ей необходимо предлагать более совершенные и интересные решения], или произошло какое-то внешнее событие, вроде прерывания - то процессор сообщает об этом VMM и… больше ничего не делает, предоставляя VMM право реагировать на возникшее событие самостоятельно. Менеджер виртуального ПК «разбирается» в происходящем, решает, как поступить (например, может, необходимо после нажатия какой-то кнопки переключить CPU на другую запущенную операционную систему), и «программно» имитирует действия центрального процессора в подобной ситуации. Причем отслеживать разные события ему приходится довольно интенсивно: «вручную» маршрутизировать обращения операционных систем к периферийным устройствам, «вручную» загружать и переключать запущенные операционные системы, «вручную» следить за виртуальной памятью запущенных операционных систем, чтобы последние нечаянно не «покалечили» друг друга, считая себя единственными претендентами на имеющиеся физические ресурсы.
Последний пример хорошо иллюстрирует суть происходящего. Как известно, современные многозадачные операционные системы активно используют механизм виртуальной памяти, когда запущенные на процессоре программы работают с «линейными» адресами, произвольным образом отображенными в реальную физическую оперативную память (или еще куда-нибудь - например, на участок жесткого диска в своп-файле или своп-разделе). С физической памятью не работает даже сама операционная система: слишком уж это неудобно - подстраиваться под особенности конкретного ПК, и слишком опасно - когда в общей куче перемешаны данные десятков и сотен запущенных программ, и только аккуратность разработчика защищает эти данные от ошибочных действий программного обеспечения. Преобразование виртуальных «линейных» адресов в физические обычно выполняет сам центральный процессор, для чего у него предусмотрена специальная таблица трансляции адресов, в которой записано, какому «линейному» адресу какой физический соответствует (или стоит пометка, что при обращении к данному участку памяти процессору следует позвать на помощь операционную систему). Хранится эта таблица не в регистрах процессора, а в оперативной памяти (в виде трех-четырехуровневого B-дерева, если вас интересуют подробности), причем таблиц может быть сколько угодно: для переключения к другому виртуальному адресному пространству в процессоре достаточно изменить один-единственный регистр CR3 (указатель на таблицу трансляции) и выполнить специальную команду сброса и очистки кэшей CPU, в которых может сохраняться старая информация о виртуальной памяти. «Простым смертным» в лице обычных программ доступ к CR3, разумеется, закрыт, и поэтому изменить «мир», в котором программы находятся, они могут только путем обращения за помощью к операционной системе. Операционной системе «проще» - она, с одной стороны, подчиняется «общим правилам», а с другой - может эти правила самостоятельно «переписывать», работая ровно в тех условиях, которые кажутся ей наиболее приемлемыми. Скажем, если ОС срочно потребуется записать что-то по физическому адресу 0x00123456, она вначале создаст в таблице трансляции своих виртуальных адресов ссылку на соответствующий участок оперативной памяти (отобразив его в то место виртуального линейного пространства ОС, которое покажется ей самым удобным) и затем обратится по свежесозданному виртуальному адресу. Для «обычного» же процесса (который, строго говоря, почти ничем не отличается от процесса ядра операционной системы) подобная «лазейка» к, скажем, «соседям» по компьютеру закрыта - ему просто-напросто не дадут увидеть собственную таблицу трансляции, выведя ее за пределы «видимости» виртуального пространства его памяти либо защитив эту область памяти от записи (здесь тоже используется та самая таблица трансляции - в ней можно указывать права доступа к виртуальным адресам). Все замечательно, все удобно, никаких проблем.
Но стоп, - скажет здесь читатель, - а что же будет, если мы запустим две операционные системы и одна из них решит отобрать для своих целей кусочек физического, а не виртуального пространства памяти другой операционной системы, отобразив его в своей таблице трансляции? В обычных условиях это фатальная ошибка, которая быстро приведет две совместно работающие операционки к краху. Но если у нас есть VMM, то ситуация в корне меняется, ибо мы теперь можем запущенную операционную систему «одурачить», перехватив соответствующее обращение и подсунув ей не настоящий CR3, а специально подготовленную «пустышку». ОС, наивно полагая, что она полностью контролирует виртуальную память компьютера, на самом деле будет всего лишь изменять никак не связанную с настоящей таблицей трансляции область оперативной памяти. А обращения к ней, используя стандартные же механизмы виртуальной памяти, будет перехватывать все тот же VMM и синхронизировать в соответствии с ними настоящую таблицу трансляции. Красиво придумано, не правда ли? Собственно виртуальной машины у нас нет, просто мы тщательно контролируем работу операционной системы и при необходимости ловко манипулируем ею.
Набор ситуаций, «прерывающих» выполнение обычных программ и приводящих к вызову VMM, довольно гибко настраивается - вовсе не обязательно, скажем, реагировать на все обращения операционных систем к компьютерным портам ввода-вывода: достаточно указать, какие порты для какой операционной системы будут «выбрасывать» ее к VMM, а какие - работать как если бы VMM не существовало в природе. Причем догадаться о том, что ОС запущена «под неусыпным контролем», практически невозможно: VMM может, например, точно таким же образом фальсифицировать обращения к CPUID (информация о процессоре и поддерживаемых им технологиях), после чего запущенная на Pentium D операционная система будет искренне полагать, что работает, скажем, на Pentium 3 и что никакой поддержки технологий виртуализации этот процессор не предоставляет.
Фальсифицируется все - даже время. Для «подделки» данных счетчика тактов TSC (Time Stamp Counter) в Vanderpool, например, предусмотрен даже не один, а целых два способа - автоматический (к значению TSC прибавляется заданная VMM константа) и «ручной» (перехватываются обращения к RDTSC); аналогично нетрудно перехватить и обращение ОС к системному таймеру с запросом текущего времени. Эдакое «1984» в рамках одного конкретно взятого процессора с VMM в роли Министерства Правды.
В качестве завершающего штриха в описании Vanderpool Technology (VT) приведем блок-схему, поясняющую функционирование технологии и назначение десяти (да-да, всего десяти!) входящих в нее инструкций (рис. 2). Заметим также, что все вышесказанное относилось к варианту VT-x для x86-совместимых процессоров. Кроме нее существует слегка отличающаяся VT-i для процессоров Itanium, но работает она по тем же самым принципам, так что останавливаться на ней я не буду.
Компьютерный мир помешался на совместимости: процессоры Intel и AMD сегодня поддерживают практически идентичные наборы инструкций, а «заклятые друзья» ревниво следят за тем, чтобы процессор конкурента никаких заметных преимуществ перед «родным» процессором не имел. Так, AMD скопировала у Intel набор инструкций SSE (1/2/3); Intel у AMD - 64-битную технологию AMD64 и входящий в ее состав NX-бит: называются они по-разному (SSE у AMD превратилась в 3Dnow! Professional; AMD64 у Intel - в EM64T), но большого значения для ПО это, по сути дела, не имеет. Просто есть некий софт (оптимизированный под SSE ли, под AMD64 - неважно), и производители процессоров стараются сделать все возможное, чтобы этот софт (независимо от того, для какого процессора он разрабатывался) мог запускаться и на их CPU. Из-за этих-то пресловутых требований совместимости до сих пор живет архитектура x86, создававшаяся скорее для микропроцессоров («ноги» i8086 растут из предназначавшегося для калькуляторов i8080) и крайне неудобная для любых современных процессоров (что Athlon, что Pentium вынуждены ее «на лету» преобразовывать в более подходящий для обработки формат). «Хоронили» ее по меньшей мере трижды - в связи с выходом процессоров «правильных» архитектур. Однако ж некогда процветавшие ветви RISC-машин сегодня зачахли, архитектура VLIW не получила должного распространения, а x86 и поныне «живее всех ее хоронивших» - колоссальный парк ПО, накопленного для этой архитектуры, сделал ее практически «непотопляемой». И в свете этого полная несовместимость технологий виртуализации от AMD и от Intel звучит как гром среди ясного неба.
Концептуально - перед нами все тот же выделенный менеджер виртуальных машин с широкими возможностями для перехвата управления у обычных операционных систем и неограниченными правами доступа. Практически - Pacifica проще, функционально богаче и «дружественнее» к разработчику VMM3 (рис. 3). Судите сами: например, можно отказаться от хитроумной и трудоемкой технологии подмены таблиц трансляции виртуальной памяти, используя двухуровневые таблицы. Обычно в таблице трансляции виртуальной памяти записываются физические адреса, но ведь там можно хранить и виртуальные адреса «второго уровня», для которых тоже будет существовать своя, определяемая исключительно VMM, таблица. То есть так же, как операционная система обеспечивает запущенным в ней программам персональные «линейные» участки виртуальной памяти, VMM просто-напросто предоставляет каждой из запущенных операционок свою «виртуальную физическую» оперативную память. И точно так же, как обычная программа не замечает подвоха в работе оперативной памяти, «одураченная» операционная система не будет подозревать, что работает она не в физическом, а в виртуальном адресном пространстве. Не нужно ничего отлавливать, перехватывать и синхронизировать - все происходит в автоматическом режиме, без малейших усилий со стороны VMM. Не совсем понятно, почему Intel отказалась от этого очевидного и радикально упрощающего жизнь программистам шага, однако в текущем варианте несчастные программисты у Intel фактически вынуждены будут дублировать основную функциональность ядра операционной системы (в вопросах, касающихся управления памятью).
Вторая принципиальная «фича» AMD’шной виртуализации - это Tagged TLB, тегированный кэш трансляции виртуальных адресов. TLB представляет собой буфер, позволяющий процессору не заниматься каждый раз чрезвычайно трудоемкой и медленной процедурой преобразования виртуального адреса в физический, а сделать это единожды и впоследствии быстро обращаться к уже вычисленным парам соответствия «виртуальная память - память реальная». Понятно, что при каждом переключении от одной программы к другой (не говоря уже о переключении от одной операционной системы к VMM и обратно), когда процессору приходится переключаться между разными виртуальными пространствами памяти, этот буфер со всей ранее накопленной информацией приходится сбрасывать - в новом виртуальном пространстве старым виртуальным адресам будут соответствовать совсем другие «физические». А значит, каждое переключение к VMM и обратно - это вопиющее расточительство процессорных ресурсов, десятки и сотни тысяч потраченных на восстановление потерянной информации тактов. В реализации AMD буфер TLB запоминает, какой из виртуальных операционных систем какой адрес принадлежит. В обычной ситуации запоминать эту информацию бессмысленно - при переключении задач TLB все равно быстро заполнится новыми адресами, вытесняющими старые; а вот для быстрого переключения от OS к VMM и обратно (когда, возможно, работа VMM не займет и сотни тактов процессорного времени) подобная оптимизация приходится как нельзя более кстати.