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

Ссылки

править