Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекция 06.docx
Скачиваний:
18
Добавлен:
26.09.2019
Размер:
1 Mб
Скачать

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

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

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

В качестве выхода из ситуации возникает вполне логичное предложение: если уж мы не можем объединить в одной операционной системе возможности нескольких разных ОС, то почему бы одновременно запустить на нашем компьютере не одну, а сразу несколько операционных систем? Заодно и надёжность повысим: если одна из операционных систем «упадёт», другая останется, и будет способна восстановить «упавшую».

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

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

Схема 5. Паравиртуализация

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

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

  • Регистры процессора (включая регистры служебного назначения).

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

  • Оперативная память.

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

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

Схема 6. Виртуализация с гостевыми ОС.

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

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

31

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]