VMM

Материал из Википедии — свободной энциклопедии
Перейти к навигации Перейти к поиску

VMM (англ. Virtual Machine Manager Диспетчер виртуальных машин)

1. Ядро операционных систем Windows версий Windows 386/3.x/95/98/Me, т.е. всех версий Windows, не являющихся Windows NT.

В ранних версиях - "многозадачник" для MS-DOS, отправлявший вниз в таковую всю реальную работу (например, с файлами на диске).

Первоначально разработан для поддержки исполнения нескольких DOS-приложений под Windows, с настоящей (preemptive) многозадачностью между ними и основной Windows (при этом между Windows-приложениями многозадачность была cooperative), в том числе в окнах. Для исполнения в окне использовалось следующее: VMM-драйвер видеокарты VDD создавал в памяти ядра виртуализацию видеокарты и видеопамяти, прилагающаяся к к нему 16-битная DLL обычных Windows - grabber DLL - умела выхватывать из него видеопамять в формате .bmp, а окно DOS - WINOLDAP.EXE - загружало grabber DLL и постоянно рисовало выхваченные растры обычными вызовами GDI в окне.

Постепенно проэволюционировал до самостоятельного ядра ОС, практически (за исключением таких пустяков в Windows 95, как работа с текущей датой и временем, см. книгу Шульмана [1]) не обращающийся вниз к DOS, и использующий DOS только в качестве загрузчика и командной строки аварийного режима.

Всегда имел структуру модульного ядра - хотя первоначально модули загружались только при старте VMM без возможности отгрузки. Кернел-модули для VMM назывались VxD, расширение файлов - .vxd или .386, формат файлов, хотя и 32битный бессегментный, отличался от WinPE формата (Win32-приложений, DLL, и .SYS кернел-модулей Windows NT), и требовал специального инструментария для построения.

Содержался (вместе со стандартным комплектом модулей VxD) в файле WIN386.EXE (до выхода Windows 95) и в VMM32.VXD (для операционных систем (Windows 9x) вместе со стандартным комплектом модулей VxD[2]

2. Virtual Memory Manager — диспетчер виртуальной памяти позволяет ОС (например, Windows 2000) использовать память на жёстком диске как часть ОЗУ. Контролирует процесс подкачки страниц с диска в ОЗУ и обратно (см. также свопинг, виртуальная память).

История[править | править код]

Первоначально была произведена разработка диспетчера виртуальных машин MS-DOS (с использованием режима V86 процессоров от 386 и выше) в виртуальном режиме процессора X86.

Ранее, в Windows 2.1 появилась версия Windows/386, включавшая в себя менеджер виртуальных машин с целью поддержки многозадачного исполнения нескольких приложений MS-DOS и их исполнения.

В Windows 3.1 появился VxD WDCTRL, который представлял собой полный драйвер режима ядра для Standard AT/IDE/ATA жесткого диска на портах 0x1f0 и 0x170. Этот драйвер поддерживал многозадачную работу с диском (процессор исполняет код, пока в диске шевелятся головки), и заменял int 13h API в BIOSе, на который полагалась MS-DOS, и который не имел никакой многозадачности (глухой цикл ожидания прерывания все время, пока диск исполняет операцию). Это называлось - 32bit disk access.

В Windows for Workgroups 3.1 в виде VxD были реализованы: SMB сервер, редиректор и протокол NetBEUI, хотя пока еще с DOSовскими драйверами сетевых карт. Поскольку редиректор является файловой системой с кэшированием, к нему в помощь был разработан vcache.386

В Windows for Workgroups 3.11 в виде VxD были реализованы: файловая система FAT (это называлось - 32bit file access), общий фасад файловых систем (FAT и редиректора) ifsmgr.386, поддержка драйверов сетевых карт в виде VxD, а также - в дополнительных компонентах - TCP/IP и Winsock. Это сделало VMM полноценным ядром ОС, практически не обращающимся к нижележащему DOSу. Шульман в своей книге [1] удивлялся, что "субминорная" смена цифры версии с .1 на .11 означала куда бОльшие изменения в ядре Windows, нежели переход на новую схему нумерации версий в Windows 95.

Примерно тогда же (около 1993 года) появился Win32S, главнейшая часть которого была реализована как VxD, и который добавлял поддержку Win32-приложений (с урезанным API) в Windows 3.x. В частности, Win32S поддерживал файлы, отображаемые в память. Главное отличие Win32S от будущей Windows 95 - это отсутствие потоков (threads), ибо потоки требовали переделки самого VMM, что было сделано лишь в Windows 95.

Win32S позволил разработчикам сравнительно легко перенести под Windows приложения из других, 32битных, ОС - например, тогдашней MacOS (Adobe APhotoshop). Невзирая на то, что тогда уже существовала Windows NT, из-за огромных по тем временам требований последней к размеру памяти (минимум 16МБ, что тогда означало - самый дорогой сегмент PC-совместимых компьютеров, почти workstation) - нетребовательный к памяти Win32S сыграл огромную роль на ИТ-рынке, позволив вырасти экосистеме Win32-приложений еще до выхода Windows 95 (и до массового перехода на Windows NT в конце 90ых, в связи с удешевлением памяти).

В Windows 95 в сам VMM была добавлена поддержка потоков. Кроме того, к нему добавились:

  • CONFIGMG - реализация реестра и подсистема Plug and Play
  • PELDR - загрузчик .sys файлов в формате Windows NT, с серьезными ограничениями на используемые API. На тот момент поддерживались только NT-драйверы сетевых карт и дисковых/SCSI контроллеров.
  • поддержка шины PCI
  • переработанная архитекура работы с дисками с добавлением scsiport.pdr - эмулятора SCSI-подсистемы из Windows NT, позволившего использовать NT-драйверы дисковых контроллеров.

В Windows 98 были добавлены:

  • NTKERN - реализация WDM - почти всего API ядра Windows NT, причем даже не NT4, а будущей Windows 2000 с поддержкой Plug and Play. Разработан на основе PELDR. При этом многие вызовы NT4, такие, например, как большинство системных вызовов ZwXxx и устарелые, предшествующие Plug and Play, вызовы для работы с аппаратурой типа HalAssignSlotResources - в WDM не вошли. Являлся оберткой над VxD API ядра VMM - например, WDM вызов IoInvalidateDeviceRelations (извещение PnP подсистемы о том, что список устройств, дочерних для данного, изменился) - был простейшей оберткой над CM_Reenumerate_DevNode в модуле CONFIGMG.
  • основанные на WDM стеки шин USD и 1394 (в то время существовали большие ожидания успеха последней). Эти стеки никогда не реализовывались как VxD. Исполняемые файлы от Windows 98 подходили к Windows 2000 (тогда в бетах), и наоборот.
  • основанная на WDM подсистема Ks - часть "медиаплеерной" подсистемы DirectShow, что находится с режиме ядра (драйвера аппаратных DVD-декодеров, подключаемых по 1394 видеокамер и др).

К концу своей эпохи VMM стал полноценным и развитым ядром ОС, будучи не очень намного хуже, чем ядро Windows NT, и почти полностью совместимым с последним и по приложениям (Win32), и по кернел-модулям (WDM).

Основные недостатки VMM:

  • отсутствие многозадачности внутри самого ядра (в Windows NT она такая же, как в Linux 2.x с CONFIG_PREEMPT, основана на спинлоках и DPC/bottom half)
  • отсутствие поддержки многопроцессорных машин
  • отсутствие "взрослых" файловых систем (наиболее умной была FAT32)
  • отсутствие overlapped IO (в Windows NT есть не только API для такового, но и все драйвера написаны как overlapped с использованием структуры IRP, что избавляет от необходимости, известной по node.js, в создании пула рабочих нитей ввода-вывода в user mode платформе).
  • отсутствие поддержки Unicode (и тем более utf-8), например, в именах файлов - использовались 1байтные (а для иероглифических языков 1-2байтные) ANSI-кодировки, при этом 2байтные иероглифические кодировки могли иметь второй байт, совпадающий с ASCII символом, чего нет в utf-8 и что требует переделки кода лексеров и парсеров).
  • проприетарный формат VxD файлов, отличный от WinPE, требовал специальных инструментов, первое время - обязательного программирования на ассемблере (обертки для Си++ появились значительно позже), и имевший весьма убогую поддержку DLL/shared library exports/imports.

Недостатки "маленьких" VMM-based Windows (которые не Windows NT) в основном не были недостатками VMM, а были связаны с тем, что эти ОС использовали 16битный графический движок GDI (и драйвера видеокарт и принтеров) и менеджер окон USER. Что касается gdi32.dll и user32.dll, то в этих версиях они были набором переходников (thunks) из плоского 32битного кода в сегментный 16битный. 16битный код GDI и USER не был thread-safe, что требовало взятия Win16Lock в обертках. Более того - при передаче управления любому 16битному приложению (все они жили в VM 0) тоже брался Win16Lock, и удерживался до передачи управления в приложение Win32 или DOS.

Это приводило к тому, например, что повисшее 16битное приложение полностью блокировало всю ОС.

Архитектура[править | править код]

Гипервизор VMM поддерживал вытесняющую многозадачность между процессами (виртуальными машинами, так как первоначально каждый процесс был экземпляром виртуальной машины DOS, кроме того, в одном из процессов VMM выполнялись все приложения Windows). С появлением Win32 (сначала в Win32S, потом в Windows 95) каждый Win32-процесс тоже стал отдельной виртуальной машиной VMM. Фактически слова "виртуальная машина" стали означать "процесс".

Внутри себя гипервизор VMM не использовал вытесняющую многозадачность - примерно так же, как Linux без CONFIG_PREEMPT, и другие UNIX-системы, особенно ранние. То есть - переключение задач делается либо явным ожиданием, либо при возврате из ядра в user mode.

Гипервизор VMM реализован на ассемблере, который также предлагался и как язык разработки дополнительных модулей (так называемые VxD). Написание модулей VxD на языке C требовало многочисленных оберток.

Обеспечивал ряд функций для модулей VxD:

  • богатый набор функций по работе со страничной памятью, в том числе возможность отобразить принадлежащий VxD блок памяти в адресное пространство виртуальной машины DOS (эмуляция видеопамяти и так далее).
  • низкоуровневые примитивы синхронизации — ожидание/пробуждение, нечто вроде condvar
  • установка перехватов на обращения к портам — при исполнении машинных команд IN/OUT в виртуальной машине управление передавалось в определенную процедуру одного из VxD.
  • установка точек останова — при попадании исполнения в виртуальной машине на точку останова управление передавалось в определенную процедуру одного из VxD.

Многозадачность и 16-битные приложения[править | править код]

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

Тем не менее Win16 API имел с этим серьёзные проблемы, полагаясь на разделяемую между задачами память. Кроме того, подсистемы GDI (двумерная графика) и USER (пользовательский интерфейс, менеджер окон) не были thread-safe. Это связано с тем, что данные подсистемы были разработаны до VMM для процессоров старше Intel 386.

Проблемы были настолько серьёзны, что не были решены до перехода на Win32, который полностью thread-safe и не полагается внутри себя, по крайней мере вне режима ядра, на разделяемую память (хотя и поддерживает её для приложений, того желающих).

Таким образом, ни в одной версии Windows не было и нет вытесняющей многозадачности между приложениями Win16. Даже в Windows NT такие приложения все исполняются в одном общем процессе NTVDM.EXE.

Что же касается Windows, основанных на VMM, то в них навсегда остались 16-битные подсистемы USER и GDI, которые вдобавок не thread-safe. 32-битные приложения захватывали мьютекс Win16Lock в прологе любого вызова этих подсистем, а при исполнении шестнадцатибитных приложений этот объект был захвачен на все время их исполнения (до отдачи процессора 32-битному приложению, которое вдобавок останавливалось в ожидании на этом объекте до тех пор, пока шестнадцатибитное приложение не осуществляло явную передачу процессора).

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

Настоящая вытесняющая многозадачность в VMM была только между виртуальными машинами MS-DOS, которые по очевидным причинам не знали о USER и GDI и никогда туда не обращались.

VxD и «настоящие» драйвера устройств[править | править код]

Обычно задачей драйвера режима ядра является полная реализация всех операций с устройством. Также обычно в ядро ОС включают модули, аналогичные драйверам, но реализующие то, что требует глобальных на всю машину данных и таблиц — TCP/IP стек, файловые системы. Также включают и те модули, которые работают в плотной связке со всем перечисленным выше (фильтры сетевых пакетов, общие полиморфные части некоторых архитектур, таких, как сокеты и т. д.).

VMM всегда разрабатывался как надстройка над MS-DOS. Что касается устройств, то приложения DOS обычно содержали в себе весь код для работы со «своими» устройствами, и VMM потому первоначально также не включал в себя драйверы устройств.

Задачей VxD первоначально было не обслуживание устройств как таковых, а сериализация представления устройства между несколькими виртуальными машинами MS-DOS. Задачей виртуального драйвера видеоадаптера (тоже VxD) также являлась полная эмуляция такового адаптера для виртуальных машин, на данный момент невидимых или изображаемых в окне.

В Windows 3.1 впервые появился модуль VxD, полностью реализующий работу с устройством — WDCTRL для PC/AT-контроллера жесткого диска (то, что впоследствии стало стандартным IDE контроллером). Возможность была показана в интерфейсе пользователя как «32-битный доступ к диску», и заключалась в полном исполнении прерываний int 13h внутри WDCTRL, который для этого сам обращался к аппаратуре, не используя BIOS и его обработчик int 13h.

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

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

Начиная с WDCTRL, VxD начали приобретать функции настоящих драйверов устройств, а VMM (хотя и по-прежнему основанный на DOS) — функции настоящего ядра ОС.

Далее в Windows for Workgroups в виде VxD был реализован весь стек протокола SMB/CIFS (с транспортным уровнем — только NetBEUI), как клиент, так и сервер, кроме самого нижнего уровня — драйвера сетевой карты, последний использовался тот же, что в MS LAN Manager Client для DOS, и загружался вместе с ядром DOS путём указания в файле config.sys.

Так как SMB клиент логически является файловой системой, появился и VxD под названием IFSMGR, распределяющий системные вызовы MS-DOS по работе с файлами (int 21h) между другими VxD, а в крайнем случае — отправляющий их в ядро DOS (того DOS, из которого загружен VMM).

В Windows 3.11 в виде VxD была реализована уже файловая система FAT (32bit File Access, улучшала производительность из-за использования страниц виртуальной памяти, а не крошечных буферов ядра DOS, под кэш). Кроме того, в этой ОС появилась возможность исполнения драйверов сетевых карт в виде модулей VxD.

Windows 95 и позже[править | править код]

Технология Plug and Play в Windows 95 была полностью реализована в виде модулей VxD, важнейший из которых является CONFIGMG.VXD.

Все драйверы устройств, участвующие в ней, были обязаны либо сами быть типа VxD, либо же иметь второй драйвер — загрузчик, знающий о первом и являющийся VxD. Второе было использовано для сред совместимости NDIS и SCSIPORT, поддерживающих загрузку драйверов сетевых карт и контроллеров устройств массовой памяти от Windows NT без изменений (даже несмотря на то, что драйверы Windows NT имели иной формат файла — тот же, что и приложения Win32).

Также в виде VxD был реализован весь стек работы с CD/DVD приводом (в том числе файловая система CDFS/Joliet), как и TCP/IP стек.

Таким образом, использование диспетчера виртуальных машин в структуре Windows 95 позволило корпорации Microsoft сделать маркетинговое заявление о том, что «Windows 95 более не использует MS-DOS», что полностью не соответствовало истине. Во-первых, исследователи (Эндрю Шульман) быстро доказали, что VMM по-прежнему обращается к нижележащему DOS для операций вроде «получить текущее время». Во-вторых, в самой ОС была возможность сделать загрузочную дискету MS-DOS, при этом использовалось то же ядро DOS, что и для загрузки VMM основной ОС.

В Windows 98 была реализована идея одинаковых драйверов для Windows 9x и Windows NT нового поколения — Windows Driver Model(WDM). Для реализации идеи в модель драйверов Windows NT добавили поддержку PnP и управления питанием (реализовано на 2 года позже Windows 98, в Windows 2000), после чего полученную модель упростили, убрав из неё некие старые вызовы времен NT 3.x, и перенесли в среду VMM.

VxD под названием NTKERN являлся «оберткой» вокруг VMM, реализующей WDM, и умеющей загружать драйверы в формате Windows NT. Например, вызов Windows NT IoInvalidateDeviceRelations (появился только в WDM, часть поддержки PnP) реализовывался через CM_Reenumerate_Devnode в CONFIGMG, и так далее.

Это позволило легко реализовать поддержку шин USB и 1394 сразу в обеих версиях Windows — все эти драйверы были реализованы в WDM. Более того, эти .sys файлы из бета-версий Windows 2000 нормально работали в Windows 98.

В те времена были различные понятия «драйвер WDM» и «драйвер Windows NT», последний мог использовать немного более богатый набор вызовов, не реализованных в NTKERN. С «вымиранием» основанных на VMM Windows исчезла и это различие, ныне WDM есть не более чем API ядра Windows для разработки драйверов аппаратуры (в противовес фильтрам сетевых пакетов, файловым системам и т. д. — такие драйвера всегда отличались коренным образом в Windows 9.x и в Windows NT).

Сравнение с настоящими виртуальными машинами[править | править код]

«Настоящие» виртуальные машины, появившиеся ещё в IBM 360, а ныне реализованные в Xen, Virtual PC, VMWare Workstation, ESX Server, Hyper-V и других продуктах, отличаются от тех виртуальных машин DOS, что были реализованы в VMM.

Главнейшее отличие - V86 (как в VMM, так и в NTVDM.EXE в Windows NT) скорее является "гнездом совместимости" со старым DOSовским кодом, а не полноценной виртуальной машиной. Например - большинство устройств, доступным приложениям, реализованы в драйверах ядра VMM или ядра Windows NT, с использованием системных вызовов для работы с ними, что не отличается от сценария "обычный процесс пользовательского режима, родной для данной ОС". Собственно, NTVDM и является полноценным Win32-приложением - DLLи для виртуализации аппаратуры в DOSовском процессе, которые в NT загружаются в user mode в процесс NTVDM.EXE (в отличие от VMM, где они загружались в ядро), имеют полный доступ ко всему Win32.

Код, исполняемый в "виртуальной машине" VMM, не мог самостоятельно войти в защищенный режим. Дело в том, что V86 - это Ring 3, а реальный режим (где такое возможно) - это Ring 0. Единственным способом это сделать было использование DPMI API int 2fh, т.е. обращение к DPMI, реализованному в DOSMGR в VMM и в NTVDM.EXE в NT, Кроме того, даже после такого входа код оставался в Ring 3, что было невозможно изменить. А это блокировало доступ к системным регистрам и аппаратным таблицам процессора, которые нужны ядру ОС защищенного режима. Таким образом, эти "виртуальные машины" не виртуализовали ни данные таблицы, ни понятие Ring, ни - что намного хуже - таблицы страниц памяти. Что же до прерываний, то виртуализировались только прерыввния реального режима, использующие таблицу по адресу 0. Нужная защищенному режиму IDT - опять же не виртуализировалась.

Потому загрузка ОС защищенного режима в "виртуальную машину" VMM (и NTVDM) - абсолютно невозможна.

Фактически эти "виртуальные машины" представляли собой некую своеобразную разновидность процесса пользовательского режима (для приложений под конкретную устаревшую ОС), что и есть "гнездо совместимости".

При этом некоторые настоящие гипервизоры, такие, как Virtual PC и VirtualBox, тоже создают своеобразный процесс пользовательского режима в ОС хоста, при этом имея и драйвер режима ядра, который делает данный процесс весьма специфичным. Но эти продукты виртуализируют и понятие Ring (2 кольца есть у любых процессоров, кроме простейших, и называются kernel mode и user mode), и таблицы страниц (настоящий гипервизор начинается с виртуализации таблиц страниц), посредством аппаратуры или же virtual TLB, и таблицы прерываний, такие, как IDT, и аппартные таблицы процессоров x86/x64.

Настоящие гипервизоры позволяют загрузить с гостевой учетной записью почти любую ОС и почти любую её версию, а также полностью перезагрузить гостевой сеанс. Для этого там эмулируется вся аппаратура, а также базовая система ввода-вывода (BIOS).

Тем не менее, виртуальные машины VMM использовали поддержку от процессора — режим V86. Настоящие же виртуальные машины требуют для своей работы либо трюков вроде virtual TLB, что приводят к гигантскому количеству «проваливаний» в гипервизор по отказу страницы и работает медленно (некоторые гипервизоры просто переключаются в режим покомандной интерпретации кода «гостя» в некоторых случаях, особенно при работе с видеоадаптером), либо же поддержку многоуровневых таблиц страниц уже в самом процессоре (Vanderpool), которая появилась не ранее примерно 2003 года (и каковая в чем-то сходна с V86).

Адекватная производительность настоящих виртуальных машин была достигнута только в поколении Pentium III, виртуальные же машины VMM прекрасно работали и на i386.

Критика[править | править код]

Отсутствие многозадачности между 16-битными приложениями Windows, вместе с использованием 16-битной подсистемы графики и пользовательского интерфейса во всех основанных на VMM Windows, а также отсутствие защиты памяти между такими приложениями, приводило к низкой надежности всех этих версий Windows.

Это вряд ли упрек в адрес VMM, скорее в адрес Win16 API и упомянутых подсистем, но тем не менее это давало мощные основания противникам Microsoft (из мира UNIX) говорить об отсутствии настоящей многозадачности в Windows.

Такое мнение распространялось даже на Windows NT, где оно есть несомненный миф, поддерживаемый разве что крайне слабой реализацией вызова fork() в пакете Cygwin, позволяющем перенос ПО из UNIX под Windows NT. Реализация fork() в Cygwin копирует всё адресное пространство, в основном из-за поддержки основанных на VMM Windows — VMM никогда не имел fork(), в отличие от ядра Windows NT (NtCreateProcess при SectionHandle == NULL), последнее использовалось в POSIX подсистеме Windows и её потомках Interix и Services for UNIX.

Windows NT по ряду возможностей — хорошая для своего времени поддержка многопроцессорных машин и наличие вытесняющей многозадачности даже внутри ядра — превосходила многие UNIX, такие, как ранние Linux и FreeBSD, в вопросе многозадачности. Впрочем, в мире Linux есть серьёзное мнение, что вытесняющая многозадачность внутри ядра не нужна.

Литература[править | править код]

Примечания[править | править код]

  1. 1 2 Эндрю Шульман. Неофициальная Windows 95
  2. Эндрю Шульман. Неофициальная Windows 95, С.79

Ссылки[править | править код]