Добавлен: 29.10.2018
Просмотров: 48206
Скачиваний: 190
536
Глава 7. Виртуализация и облако
VT-технологии выполнение инструкции завершается ошибкой, и операционная систе-
ма попадает в аварийную ситуацию. На центральных процессорах с VT-технологией
при выполнении гостевой операционной системой служебной инструкции происходит
системное прерывание с передачей управления гипервизору (см. рис. 7.2). Затем ги-
первизор может проверить инструкцию, чтобы определить, была она выдана гостевой
операционной системой в виртуальной машине или же пользовательской программой
на виртуальной машине. В первом случае он организует выполнение инструкции,
а в последнем — эмулирует то, что сделало бы настоящее оборудование при встрече со
служебной инструкцией, выполняемой в пользовательском режиме.
7.4.1. Виртуализация оборудования,
не готового к виртуализации
Создание виртуальной машины при доступности VT-технологии особых вопросов
не вызывает, но что делали люди до ее появления? Например, компания VMware
реализовала гипервизор задолго до появления виртуализационных расширений на
x86. И опять ответ заключается в том, что разработчики программного обеспечения,
создавшие такие системы, очень разумно распорядились двоичной трансляцией (binary
translation) и свойствами оборудования, имевшегося на x86, такого как кольца защиты
(protection rings) процессора.
Многие годы в x86 поддерживались четыре режима, или кольца, защиты. Кольцо 3
наименее привилегированное. В нем выполняются обычные пользовательские про-
цессы. В этом кольце невозможно выполнить привилегированные инструкции.
Кольцо 0 наиболее привилегированное, позволяющее выполнять любую инструк-
цию. В нормальных условиях ядро работает в кольце 0. Остальные два кольца ни
одной текущей операционной системой не используются. Иными словами, гипер-
визоры могли свободно использовать их по своему усмотрению. Как показано на
рис. 7.3, вследствие этого многие решения по виртуализации содержали гипервизор
в режиме ядра (кольцо 0), приложения — в пользовательском режиме (кольцо 3),
а гостевую операционную систему помещали на уровень с промежуточной приви-
легией (кольцо 1). В результате ядро имело более высокую привилегированность
по отношению к пользовательским процессам, и любая попытка доступа к памяти
ядра из пользовательской программы приводила к нарушению прав доступа. В то же
время привилегированные инструкции гостевой операционной системы вызывали
системное прерывание с передачей управления гипервизору. Гипервизор проводил
ряд проверок корректности, а затем выполнял инструкции от имени гостевой опе-
рационной системы.
Что касается служебных инструкций в коде ядра гостевой операционной системы, то
гипервизор гарантирует прекращение их дальнейшего существования. С этой целью
он переписывает код, обрабатывая в каждый момент времени только один базовый
блок
(basic block), представляющий собой небольшую прямую последовательность
инструкций, завершающуюся переходом. По определению, в базовом блоке нет пере-
ходов, вызовов, системных прерываний или других инструкций, вызывающих пере-
дачу управления, за исключением самой последней инструкции, которая именно это
и делает. Прямо перед выполнением базового блока гипервизор сначала сканирует его,
чтобы определить, не содержит ли он служебных инструкций (в толковании Попека
и Голдберга), и, если таковые имеются, заменяет их вызовом процедуры гипервизора,
7.4. Технологии эффективной виртуализации
537
Гипервизор первого типа
Виртуальная
машина
Гостевая операционная система
(Переписывание двоичного кода,
предшествующее выполнению + эмуляция)
Кольцо 0
Кольцо 1
Кольцо 2
Кольцо 3
Оборудование
Пользовательский процесс
Рис. 7.3. Двоичный транслятор переписывает инструкции гостевой операционный системы,
работающей в кольце 1, в то время как гипервизор работает в кольце 0
занимающейся их обработкой. Переход в последней инструкции также заменяется
вызовом, направленным в гипервизор (чтобы гарантировать возможность повторения
процедуры для следующего базового блока). Динамическая трансляция и эмуляция
представляются весьма затратным делом, но обычно это не так. Транслируемые бло-
ки кэшируются, что исключает их трансляцию в будущем. Кроме того, большинство
блоков кода не содержат служебных или привилегированных инструкций и поэтому
могут выполняться обычным образом. В частности, при условии, что гипервизор про-
изводит тщательную настройку аппаратного обеспечения (как это делает, к примеру,
VMware), двоичный транслятор может игнорировать все пользовательские процессы.
Они в любом случае выполняются в непривилегированном режиме.
После того как выполнение базового блока завершится, управление возвращается
гипервизору, который затем находит его преемника. Если этот преемник уже был
оттранслирован, он может быть выполнен без промедлений. В противном случае он
сначала проходит трансляцию, кэшируется, а затем выполняется. В итоге основная
часть программы попадет в кэш и будет выполняться практически на полной скорости.
Используются различные оптимизации: например, если базовый блок завершается ин-
струкцией перехода на другой базовый блок (или инструкцией его вызова), последняя
инструкция может быть заменена переходом непосредственно на оттранслированный
базовый блок, исключая все издержки, связанные с поиском блока-преемника. Опять
же исключается необходимость замены служебных инструкций в пользовательских
программах, оборудование в любом случае будет их просто игнорировать.
В то же время двоичная трансляция довольно часто применяется ко всему коду го-
стевой операционной системы, выполняемой в кольце 1, и при этом даже заменяются
привилегированные служебные инструкции, что в принципе может быть сделано также
при обработке служебного прерывания. Причина в том, что эти служебные прерывания
обходятся очень дорого, а двоичная трансляция приводит к достижению более высокой
производительности.
Все сказанное до сих пор относилось к гипервизорам первого типа. Хотя концептуально
гипервизоры второго типа отличаются от гипервизоров первого типа, в целом в них
используются те же самые технологии. Например, VMware ESX Server (гипервизор,
впервые поставленный в 2001 году) использует в точности такую же двоичную транс-
538
Глава 7. Виртуализация и облако
ляцию, как и первая версия VMware Workstation (гипервизор второго типа, выпущен-
ный двумя годами ранее).
Но запуск кода гостевой операционной системы обычным порядком и использова-
ние в точности таких же технологий требуют от гипервизора второго типа искусного
управления оборудованием на самом низком уровне, что невозможно сделать из поль-
зовательского пространства. Например, нужно установить дескрипторы сегментов
в точности на правильное значение для гостевого кода. Для точной виртуализации
гостевая операционная система должна быть введена в заблуждение и полагать, что она
воистину царь горы и имеет полный контроль над всеми ресурсами машины, а также
доступ ко всему адресному пространству (4 Гбайт на 32-разрядной машине). Когда
царь обнаружит присутствие другого царя (ядра основной операционной системы),
незаконно вселившегося в его адресное пространство, он не будет удивлен.
К сожалению, именно так и происходит, когда гостевая операционная система запуска-
ется в качестве пользовательского процесса на обычной операционной системе. Напри-
мер, в Linux пользовательский процесс имеет доступ лишь к 3 Гбайт из 4-гигабайтного
адресного пространства, поскольку оставшийся 1 Гбайт зарезервирован под ядро.
Любое обращение к памяти ядра приводит к системному прерыванию. В принципе,
есть возможность перехватить системное прерывание и эмулировать соответствующие
действия, но это обходится слишком дорого и обычно требует установки соответству-
ющего обработчика системного прерывания в ядро основной операционной системы.
Другой (общеизвестный) способ решения проблемы двух царей заключается в пере-
конфигурации системы для удаления основной операционной системы и фактическом
предоставлении гостевой операционной системе всего адресного пространства. Но сде-
лать это из пользовательского пространства в принципе невозможно.
Также, чтобы все сделать правильно, гипервизору нужно обрабатывать прерывания,
например, когда диск выдает прерывание или когда происходит ошибка отсутствия
страницы. Кроме того, если гипервизору нужно воспользоваться передачей управления
при системном прерывании и эмуляцией для привилегированных инструкций, ему
нужно получать системные прерывания. К тому же пользовательские процессы не
могут устанавливать обработчики системных и обычных прерываний в ядро.
Поэтому у большинства современных гипервизоров второго типа имеется модуль ядра,
действующий в кольце 0, который позволяет им искусно управлять оборудованием при
выполнении привилегированных инструкций. Конечно, в управлении оборудованием
на самом низком уровне и предоставлении гостевой операционной системе доступа ко
всему адресному пространству нет ничего плохого, но рано или поздно гипервизору
понадобится его очистить и восстановить исходный контекст процессора. Допустим,
к примеру, что во время работы гостевой операционной системы поступило прерывание
от внешнего устройства. Поскольку гипервизор второго типа зависит от драйверов
устройств основной операционной системы, для запуска кода гостевой операционной
системы ему требуется полная переконфигурация оборудования. При запуске драйве-
ра устройства он находит все им ожидаемое. Гипервизор ведет себя как тинейджеры,
устраивающие вечеринку, пока родителей нет дома. И нет ничего страшного в полной
перестановке мебели при условии, что к возвращению родителей все будет возвращено
на прежние места. Переход от конфигурации оборудования для ядра основной опера-
ционной системы к конфигурации для гостевой операционной системы известен как
переключатель мира
(world switch). Более подробно он будет рассмотрен при изучении
VMware в разделе 7.12.
7.4. Технологии эффективной виртуализации
539
Теперь должно быть понятно, как эти гипервизоры работают даже на оборудовании,
не готовом к виртуализации: служебные инструкции в ядре гостевой операционной
системы заменяются вызовами процедур, эмулирующих эти инструкции. Теперь ни
одна служебная инструкция, выданная гостевой операционной системой напрямую,
настоящим оборудованием не выполняется. Эти инструкции превращаются в вызовы
гипервизора, который затем эмулирует их.
7.4.2. Цена виртуализации
Можно было бы наивно полагать, что центральные процессоры с VT существенно
превзойдут программные технологии, прибегающие к трансляции, но замеры дают
неоднозначную картину (Adams and Agesen, 2006). Оказывается, подход, предусматри-
вающий передачу управления при системном прерывании и эмуляцию, используемый
VT-оборудованием, приводит к выдаче большого количества системных прерываний,
которые на современном оборудовании обходятся очень дорого, поскольку разруша-
ют кэши центрального процессора, TLB-буферы и таблицы предсказания переходов,
находящиеся внутри центрального процессора. В отличие от этого, когда служебные
инструкции внутри выполняемого процесса заменяются вызовами процедур гипервизо-
ра, таких издержек от контекстного переключения не происходит. Как показали Адамс
и Агесен, в зависимости от загруженности программный вариант порой превосходит
аппаратный. Поэтому некоторые гипервизоры первого (и второго) типа во избежание
падения производительности осуществляют двоичную трансляцию, даже если про-
грамма будет правильно выполняться и без нее.
При осуществлении двоичной трансляции сам оттранслированный код по сравнению
с исходным кодом может быть либо более медленным, либо более быстрым в выполне-
нии. Предположим, к примеру, что гостевая операционная система выключила аппарат-
ные прерывания, используя инструкцию CLI (clear interrupts — очистить прерывания).
В зависимости от архитектуры эта инструкция может выполняться очень медленно,
занимая десятки тактов на конкретных центральных процессорах с глубокими кон-
вейерами и выполнением инструкций вразброс. Теперь уже должно стать понятно,
что желание гостевой операционной системы заблокировать прерывания не означает,
что гипервизор на самом деле должен их заблокировать и повлиять на работу всей ма-
шины. Соответственно гипервизор должен выключить их для гостевой операционной
системы без реального выключения. Для этого он может отслеживать специальный
флаг прерываний (Interrupt Flag (IF)) в структуре данных в виртуальном центральном
процессоре, который им поддерживается для каждой гостевой операционной системы
(гарантируя тем самым, что виртуальная машина не получает никаких прерываний
до тех пор, пока прерывания опять не будут включены). Каждое появление CLI в го-
стевой операционной системе будет заменено чем-нибудь вроде VirtualCPU.IF = 0, то
есть весьма малозатратной инструкцией перемещения, которая займет не более трех
тактов. Следовательно, оттранслированный код будет выполнен быстрее. Тем не менее
при использовании современного VT-оборудования аппаратное решение превосходит
по эффективности программное.
В то же время если гостевая операционная система модифицирует свои таблицы стра-
ниц, это обходится очень дорого. Проблема в том, что каждая гостевая операционная
система на виртуальной машине полагает, что имеет дело со своей «собственной»
машиной и может свободно отображать любую виртуальную страницу на любую фи-
зическую страницу в памяти. Но если одна виртуальная машина хочет использовать
540
Глава 7. Виртуализация и облако
физическую страницу, которая уже используется другой виртуальной машиной (или
гипервизором), кто-то должен ее выделить. В разделе 7.6 показано, что решением будет
добавление еще одного уровня таблиц страниц для отображения гостевых физических
страниц на реальные физические страницы на основной машине. Неудивительно, что
возня с несколькими уровнями таблиц страниц обходится недешево.
7.5. Являются ли гипервизоры
настоящими микроядрами?
Оба гипервизора, как первого, так и второго типа, работают с немодифицированными
гостевыми операционными системами, но для достижения высокой производитель-
ности должны «прыгать сквозь обручи». Как уже было показано, в паравиртуализации
используется другой подход, предусматривающий вместо этого модификацию исходно-
го кода гостевой операционной системы. Вместо выполнения служебных инструкций
паравиртуализированная гостевая операционная система выполняет гипервызов.
Получается, что гостевая операционная система работает наподобие пользовательской
программы, совершая системные вызовы к операционной системе (гипервизору). Когда
выбран этот маршрут, гипервизор должен определить интерфейс, состоящий из набора
вызовов процедур, которыми может воспользоваться гостевая операционная система.
Этот набор вызовов, который, по сути, не что иное, как программный интерфейс при-
ложения (Application Programming Interface (API)), является при этом интерфейсом,
предназначенным для использования гостевой операционной системой, а не приклад-
ными программами.
Если сделать еще один шаг вперед и убрать из операционной системы все служебные
инструкции, просто заставив ее совершать гипервызовы для получения системных
служб ввода-вывода, гипервизор превратится в микроядро, подобное тому, что изобра-
жено на рис. 1.26. При исследовании паравиртуализации оказалось, что идея эмуляции
специфических аппаратных инструкций является скучной и трудоемкой задачей. Куда
лучше для выполнения ввода-вывода и прочих подобных задач заставить гостевую
операционную систему вызвать гипервизор (или микроядро).
Действительно, некоторые исследователи утверждают, то гипервизоры, видимо, нужно
рассматривать как «настоящие микроядра» (Hand et al., 2005). Прежде всего следует
заметить, что это весьма спорный вопрос и другие исследователи активно выступают
против этого понятия, утверждая, что разница между ними изначально не носит фунда-
ментального характера (Heiser et al., 2006). Третьи намекают, что по сравнению с микро-
ядрами гипервизоры не могут быть приспособлены для построения безопасных систем,
и рекомендуют расширить их функции, придав им такие функциональные возможности
ядра, как передача сообщений и совместное использование памяти (Hohmuth et al., 2004).
И наконец, есть такие исследователи, которые соглашаются с тем, что гипервизоры даже
не являются продуктом «настоящих исследований операционных систем» (Roscoe et al.,
2007). Поскольку пока никто ничего не сказал о верно (или неверно) составленных (на-
стоящих) руководствах по операционным системам, мы посчитали, что будет правильно
исследовать схожесть гипервизоров и микроядер немного глубже.
Основной причиной того, что первые гипервизоры эмулировали всю машину, было
отсутствие доступа к исходному коду гостевой операционной системы (например,
к коду Windows) или слишком большое количество вариантов такого кода (например,