От переводчика: представляю вашему вниманию перевод статьи Майкла Штейла. Я давно хотел подготовить подобный обзор методов использования виртуализации для задач обеспечения совместимости. Я даже опубликовал некоторые заметки на эту тему: в учебнике по симуляции, глава 1, и на Хабре в посте про системные ВМ. Однако мне не удалось раскрыть вопрос так глубоко, как он представлен в этой работе. Поэтому я решил поделиться переводом с читателями.
Производители операционных систем сталкиваются с этой проблемой один или два раза в десятилетие: им необходимо перевести свою пользовательскую базу со старой операционной системы на их сильно отличающуюся новую ОС, или им требуется перейти с одной архитектуры ЦПУ на другую с сохранением возможности запуска старых приложений без модификаций, а также помочь сторонним разработчикам портировать свои приложения на новую ОС.
Давайте рассмотрим, как это происходило в последние 30 лет, на примерах MS DOS/Windows, Apple Macintosh, Amiga OS и Palm OS.
CP/M — 8-битная ОС от фирмы Digital Research, работавшая на всех вариантах систем на основе Intel 8080. 86-DOS от Seattle Computer Products, позже ставшая известной как MS-DOS (именуемая также PC-DOS на машинах от IBM), была клоном CP/M для более позднего Intel 8086; аналогичная ситуация была и с CP/M-86 от самой DR (эта ОС позже была названа DR-DOS).
Не являясь совместимым на двоичном уровне, Intel 8086 был «совместим на уровне ассемблера» с 8080. Это означало, что было легко сконвертировать 8-битную программу, написанную на ассемблере для 8080 или Z80, в программу на ассемблере для 8086, так как эти две архитектуры были очень похожи (обратно-совместимая модель памяти, легко отображаемые друг на друга наборы регистров), различались лишь машинные кодировки команд.
Так как MS-DOS использовала те же самые соглашения ABI и карту памяти, эта ОС была совместима на уровне исходных кодов с CP/M. На системах CP/M, имеющих доступ максимум к 64 кБайт памяти, диапазон адресов с 0x0000 по 0x0100 (нулевая страница) была зарезервирована под нужды ОС и содержала, кроме прочего, аргументы командной строки. Исполняемое приложение располагалось, начиная с адреса 0x0100 и выше, а ОС размещалась в верхней части памяти; стек приложения рос вниз, начинаясь чуть ниже начала ОС. Модель памяти 8086 разбивает память на (перекрывающиеся) непрерывные сегменты длиной в 64 кбайт, так что каждый из этих сегментов фактически достаточен для виртуальной машины 8080. Поэтому исполняемые файлы .COM MS-DOS имеют размер до 64 кбайт и загружаются в память, начиная с адреса 0x100. Диапазон 0x0 — 0x100 в MS-DOS именуется Program Segment Prefix и он очень похож на нулевую страницу CP/M. Из-за высокой степени совместимости этих процессоров и их ABI перенос программы с CP/M в DOS происходил относительно безболезненно. Однако, такой порт мог использовать максимум 64 кбайт памяти (примеры — Wordstar 3.0 1982 года). Но это позволяло иметь единую кодовую приложения базу для обеих ОС с помощью лишь нескольких макроопределений и двух различных программ-ассемблеров.
MS-DOS 2.0 принесла с собой новые, более мощные соглашения API (хэндлы для файлов вместо структур FСB, поддиректории, формат .EXE) и перевела большую часть API от CP/M в статус устаревших. Но DOS сохраняла совместимость с CP/M вплоть до последней версии.
Изначально Microsoft Windows была спроектирована как графическая оболочка поверх MS-DOS. Вся работа с устройствами и файловой системой происходила через вызовы DOS API. Поэтому все драйверы MS-DOS исполнялись напрямую, и Windows могла использовать их. Приложения DOS всё ещё могли запускаться после выхода из Windows в чистый DOS.
В Windows/386 2.1 модель была изменена. Это уже было настоящее ядро ОС, исполнявшее множество виртуальных машин в режиме virtual 8086 (V86) бок о бок; одна была выделена для сессии ОС MS-DOS, и по одной выделялось для каждого приложения Windows. Виртуальная машина DOS использовалась Windows для обращения к драйверам устройств и файловой системы, так что это был практически слой совместимости драйверов, исполняемый внутри ВМ. Пользователь мог запустить любое число дополнительных ВМ DOS для работы с DOS-приложениями; и каждая при этом содержала свою копию DOS. Windows перехватывала обращения с памяти экрана, а также некоторые системные вызовы, и перенаправляла их с графический драйвер Windows или в первую «главную» ВМ DOS.
Windows 3.x начала использовать родные драйвера для Windows, которые заменили обращения внутрь DOS ВМ, а также начала перенаправлять некоторые вызовы DOS к устройствам внутрь Windows. Стандартная установка Windows 95 уже совсем не использовала обращения к DOS ВМ для нужд работы с драйверами и файловой системой, однако при необходимости такой режим мог ещё мог быть использован.
DOS являлась не только средой обеспечения совместимости для старых драйверов и приложений, она также предоставляла командную строку Windows, поэтому, когда в Windows 95 появилась поддержка длинных имён файлов, пришлось перехватывать вызовы API DOS для предоставления новой функциональности для утилит командной строки.
Системы семейства Windows NT не базировались на DOS, тем не менее с самой первой версии они позволяли исполнять приложения MS-DOS. Как и в случае не-NT варианта Windows, в таком случае DOS-программа работала в V86-режиме процессора. Однако, вместо использования полной копии MS-DOS внутри ВМ, NT помещала в неё только код приложения, перехватывала все системные вызовы и обращения к устройствам и оборачивала их в вызовы NT API. Фактически, этот способ не является чистой ВМ: режим V86 использовался только для создания необходимой для поддержки DOS-приложений модели памяти.
Есть широко распространённое заблуждение, что командная строка Windows NT — это «оболочка DOS». На самом деле интерпретатор и вспомогательные утилиты были родными NT-приложениями, и подсистема NTDVM (виртуальная машина DOS) вообще не стартовала до тех пор, пока действительно не возникала необходимость в запуске DOS-программы из командной строки.
Начиная с релиза Windows NT 3.1 в 1993 году стало понятно, что она в конце концов заменит классическую Windows. Несмотря на то, что NT имела одинаковый с Win16 пользовательский интерфейс и хорошую совместимость с DOS, каждая выходившая новая версия обычно требовала для работы довольно мощный компьютер. Поэтому миграция с Windows на Windows NT проходила обратным образом: каждая версия Windows становилась всё больше похожей на NT до тех пор, пока они не стали достаточно похожими. К тому времени даже слабые компьютеры той эпохи стали достаточно мощными для работы под NT.
Большим шагом по сближению Windows и Windows NT стала поддержка родного для NT Win32 API. Первым этапом было бесплатное обновление «Win32S» к Windows 3.1, которое предоставляло подмножество (отсюда «S» — subset — в названии) Win32 API на классической Windows. Win32S расширило ядро Windows возможностью создания 32-битных адресных пространств для всех 32-битных приложений (в NT для каждого приложения создавалось собственное пространство). Оно также сделало доступным версии некоторых библиотек из NT (например, RICHED32.DLL), а также 32-битные DLL, принимавшие вызовы из низкоуровневых Win32API вызовов (подсистемы «GDI» и «USER»), автоматически траслировались в вызовы подсистемы Win16 («thunking»).
В Windows 95 эта функциональность была доступна по умолчанию. Она также использовала изолированные адресные пространства для 32-битных приложений, поддерживала большее подмножество Win32 API. Некоторые базовые приложения стали 32-битными (например, Проводник), однако значительная часть базовой системы всё ещё была 16-битной. С появлением Windows 95 большинство разработчиков переключились на создание 32-битных приложений, что позволяло также компилировать их и на системах Windows NT.
Второй шаг по миграции с 16-битной Windows на Windows NT произошёл при переходе от Windows ME на Windows XP (NT-семейство) в 2001. Windows NT (2000, XP...) была полностью 32-битной ОС с Win32 API, но также позволяла исполнять 16-битные приложения Windows через трансляцию вызовов Win16 в Win32 (thunking).
Модель драйверов Windows NT 3.1/3.5/4.0 (Windows NT Driver Model) отличалась от подхода классической Windows (VxD), поэтому Windows 98 (наследник Windows 95) и Windows 2000 (наследник Windows 4.0) обе поддерживали новую «Модель драйверов Windows». Один и тот же драйвер теперь мог работать в обеих ОС, но каждая при этом продолжала поддерживать и свой оригинальный режим для старых драйверов.
Когда Microsoft перевела домашних пользователей на системы NT, большинство приложений, игр и драйверов заработали на Windows XP. Единственное, что пришлось переписывать — это системные утилиты.
Переход от 32-битных Windows к 64-битным происходит в настоящее время. Windows XP была первой ОС от Microsoft, доступной для архитектуры Intel 64/AMD64, все последующие системы (Vista, 7, 8, 10) доступны в вариантах для 32 и 64 бит. В 64-битных редакциях ядро работает в 64-битном режиме, как и все системные библиотеки и большинство приложений. API для 32 бит поддерживается через подсистему WOW64 (Windows-on-Windows 64). 32-битное приложение линкуется с 32-битными библиотеками, однако низкоуровневые вызовы API транслируются через WOW64 в аналоги для 64-битных DLL.
Так как драйверы исполняются в том же адресном пространстве, что и ядро, было трудно обеспечить поддержку работы 32-битных драйверов, поэтому её нет. Также была прекращена поддержка приложений DOS и Win16 в 64-битных вариантах ОС.
Apple перевела свои компьютеры с процессоров Motorola 68k на Motorola/IBM PowerPC в период с 1994 по 1996 год. Так как операционная система для Macintosh, System 7, была в основном написана на ассемблере для 68k, она не могла быть легко сконвертирована в ОС для PowerPC. Вместо этого, большая часть её исполнялась через симуляцию. Новое «наноядро» получало и обрабатывало прерывания и выполняло минимальное управление памятью, абстрагируя особенности PowerPC, а интегрированный эмулятор 68k исполнял старую ОС, которая была модифицирована для связывания с наноядром. Таким образом, System 7.1.2 для PowerPC была фактически паравиртуализована и исполнялась поверх очень тонкой прослойки гипервизора.
Первая версия Mac OS для PowerPC исполняла большую часть системного кода внутри эмулятора для 68k, включая драйверы. Часть кода, критичного для обеспечения производительности, была переписана на прямое исполнение. Загрузчик исполняемых файлов мог детектировать программы с PowerPC и мог исполнять их напрямую. Большая часть коммуникаций с ОС всё равно шла через эмулятор. В более поздних версиях Mac OS всё большая доля кода переписывалась с 68k на PowerPC.
Аналогично тому, как Microsoft перевела пользователей с Windows на Windows NT, Apple перешла с классической Mac OS на Mac OS X. Тогда как классическая Mac OS была аляповатой (hackish) ОС с кооперативной многозадачностью, без защиты памяти, и при этом всё ещё исполняла часть системного кода внутри эмулятора 68k, Mac OS X была основана на NEXTSTEP, современной UNIX-подобной ОС с полностью отличающимся API.
Когда в Apple решили провести миграцию, они портировали системные библиотеки классической Mac OS («Toolbox»), при этом убрав вызовы, которые не могли быть поддержаны на современной ОС, заменив их альтернативами, и назвали новое API «Carbon». То же самое API стало доступно в классической Mac OS 8.1 в 1998, так что разработчики смогли обновить свои приложения для работы в Mac OS X, сохранив при этом совместимость с классической Mac OS. Когда Mac OS X стала публично доступна в 2004 году, «карбонизированные» к этому моменту приложения могли работать на обеих ОС без перекомпиляции. Это напоминает подход Microsoft по приближению классической Windows к Windows NT.
Но так как нельзя было ожидать, что все приложения будут сконвертированы в «карбонизированную» форму к моменту появления Mac OS X, новая ОС также содержала виртуальную машину «Classic» или «Blue Box», в которой немодифицированная Mac OS 9 исполнялась и могла поддерживать произвольное число старых приложений. Запросы к сети и файловой системе внутри ВМ перехватывались и перенаправлялись в хозяйскую ОС, а интеграция с оконным менеджером позволяла двум средам рабочего стола выглядеть практически бесшовно.
В 2005 году Apple анонсировала второй переход с одной архитектуры ЦПУ на другую, на этот раз с PowerPC на Intel IA-32. Так как эта ОС была в основном написана на C и Objective C, она могла легко быть портирована на IA-32. В Apple утверждают, что они всегда поддерживали вариант ОС для IA-32, начиная с самого первого релиза.
Для того, чтобы исполнять старые приложения, ещё не портированные на IA-32, Apple включила эмулятор Rosetta в состав новой ОС. В этот раз он не был тесно интегрирован с ядром, как это было в случае перехода с 68k на PowerPC. Ядро предоставляло лишь возможность запуска внешнего рекомпилятора для приложения всякий раз, когда оно оказывалось предназначенным для PowerPC. Rosetta транслировала весь код приложения и код библиотек и предоставляла интерфейс для взаимодействия с ядром ОС.
Следующий переход для Apple заключался в миграции с 32-битной архитектуры Intel на её 64-битный вариант. Он произошёл в Mac OS X 10.4 в 2006 году. Хотя ОС целиком могла бы быть портирована на 64 бита, как это было сделано в случае Windows, Apple решила использовать подход, более похожий на переход на Windows 95: ядро осталось 32-битным, но получило поддержку 64-битных приложений. Все системные приложения и драйверы остались 32-битными, но некоторые библиотеки также получили 64-битные варианты. Таким образом, 64-битное приложение линковалось с 64-битными библиотеками и выполняло 64-битные системные вызовы, которые при этом конвертировались с 32-битные внутри ядра.
Mac OS X 10.5 начала поставлять все библиотеки в 64-битном варианте, ядро осталось 32-битным. В версии 10.6 ядро было переведено на 64 бита, и использование 64-битных драйверов стало обязательным.
Платформа Amiga использовала одну и ту же ОС для ЦПУ 68k в эпоху Commodore с 1985 по 1994 год. Однако, с 1997 года для неё существовал апгрейд на плату с ЦПУ архитектуры PowerPC. Эта операционная система не предоставляла свой исходный код для сторонних, поэтому её невозможно было портировать на PowerPC. По этой причина сама Amiga OS продолжала работать на выделенном чипе 68k, и расширенный загрузчик программ детектировал PowerPC код и передавал его на второй ЦПУ. Все системные вызовы маршрутизировались через библиотечную прослойку обратно на 68k.
AmigaOS 4 (2006 год) является настоящим портом AmigaOS на PowerPC. Этот переход потребовал больших усилий, так как много исходного кода ОС пришлось сперва конвертировать c языка BCPL в язык Си. Поддержка приложений для 68k сделана через эмуляцию двоичного кода и пробросу API вызовов.
Palm перешёл с процессоров 68k на ARM в версии Palm OS 5 в 2002 году. ОС была портирована на ARM, и эмулятор для 68k «Palm Application Compatibility Environment» («PACE») был включён в её состав для исполнения старых приложений. Однако, Palm не рекомендовала разработчиками переключаться на написание приложений для ARM и даже не предоставляла среды для исполнения таких приложений под управлением ОС. Утверждалось, что большинство приложений для Palm OS большую часть времени проводили в (уже оптимизированном «родном») коде операционной системы, и поэтому они не получили бы ощутимого выигрыша в скорости при портировании.
Однако для приложений, интенсивно использующих ЦПУ или содержащих код для сжатия или шифрования данных, Palm предоставила метод исполнения небольших кусочков ARM-кода внутри приложения для 68k. Подобные ARMlet'ы (позже названные PNOlet'ы — «Palm Native Object») можно было вызывать из кода для 68k. Они предоставляли минимальный интерфейс передачи данных (одно целое число на вход и одно — на выход). Разработчикам самим приходилось заботиться о передаче больших структур в памяти и решать проблемы порядка байт (endianness) и их выравнивания. Процедуры из ARM-кода не могли ни вызывать код для 68k, ни делать API вызовы операционной системы.
Привязка к архитектуре 68k (не являющейся родной для используемого железа) как основной для большинства приложений фактически означал использование виртуальной архитектуры пользовательских приложений, на манер Java или .NET. Переход на ARM был практически незаметен для сторонних разработчиков, и в теории такой подход позволял Palm вновь менять хозяйскую архитектуру при необходимости в будущем с минимальными затратами.
Давайте ещё пробежимся по рассмотренным переходам между ОС и ЦПУ и способам, как различные вендоры подходили к задаче миграции.
Переход к новому режиму процессора — самый простой для ОС, так как старые приложения могут продолжать исполняться напрямую, а вызовы API могут быть транслированы. При этом сама ОС может или продолжить использовать старый режим, конвертируя системные вызовы из новых приложений в старый формат, или же перейти на новую разрядность и конвертировать вызовы старых приложений. Также есть два способа перехватывать сами вызовы. Можно перехватывать высокоуровневые вызовы API (такие как создание GUI-окна), но это сложно выполнить, так как таких вызовов много, и сложно написать корректный конвертор для столь большого числа функций.
Альтернативно, ОС может конвертировать низкоуровневые системные вызовы. В этом случае перехватываемый интерфейс очень узкий. Но, так как старые приложения линкуются со старыми библиотеками, а новые — с новыми библиотеками, их хранение и использование потребляет в два раза больше места (памяти) в случаях, когда пользователь исполняет старые и новые приложения одновременно.
В случае перехода с 16 до 32 бит в Windows, ОС осталась 16-битной, а вызовы конвертировались из 32-битного формата в 16-битное API. Когда Windows NT переводили с 32 до 64 бит, вся ОС стала 64-битной, а старые системные вызовы конвертировались в новые. Тот же переход был выполнен иначе в случае Mac OS X: ОС осталась 32-битной, а 64-битные системные вызовы транслировались на уровне ядра.
Подходы, использованные для Windows NT и Mac OS X похожи, так как в обоих случаях 32-битные приложения продолжили использовать 32-битные библиотеки, а 64-битные — новые 64-битные библиотеки. Различие были в режиме нового ядра. В случае Windows возникало преимущество в виде возможности использования более 4 Гбайт памяти в режиме ядра и некоторое ускорение от использования нового 64-битного набора регистров. В случае Mac OS X, осталась возможность продолжать использовать старые 32-битные драйвера без модификаций. И да, вторым шагом Mac OS X позже полностью перешла на 64-битное ядро и драйверы.
Сменить используемый процессор сложнее, так как на новой архитектуре нельзя больше исполнять старый машинный код. Некоторые ОС не так-то просто было перенести.
Mac OS X для Intel и Palm OS для ARM были написаны в достаточно платформенно-независимой манере, чтобы быть переносимой на новую архитектуру. Обе включали в себя рекомпиляторы, которые исполняли старый код. Это наиболее простой подход. Amiga OS не могла быть портирована таким образом, так как её исходный код не был доступен. Поэтому система включала оба процессора — оригинальная ОС и её код исполнялся на старом процессоре, а на новом процессоре работал новый код, отправляя системные вызовы на старый ЦПУ.
Для классической Macintosh (с 68k на PowerPC) исходный код был доступен, но всё равно не мог быть легко изменён. Поэтому переход был выполнен аналогично ситуации с Amiga, но на одном ЦПУ. Большая часть ОС исполнялась внутри эмулятора, новые приложения запускались напрямую, вызывая системные вызовы обратно из эмулятора.
MS-DOS был реализацией старой ОС, выполненный другой компанией. Она не поддерживала исполнение старого двоичного кода. Вместо этого, разработчикам предлагалось перекомпилировать свои приложения.
Переход на новую ОС с сохранением всех пользователей и программ — самый сложный из рассматриваемых.
Выбираемый подход зависит от планов поддержки старого API от старой ОС. Если API было достаточно хорошо, чтобы его стоило поддерживать в новой ОС, новая ОС должна просто поддерживать то же самое API. Это выполнялось для случаев переходов от CP/M к DOS и от Windows 9x к Windows NT. В каком-то смысле это выполнялось для перехода с классической Mac OS на Mac OS X, но в этом случае Carbon не являлся главным API новой ОС, а лишь одним из трёх поддерживаемых (Carbon, Cocoa, Java). Практически всё кроме Cocoa, в настоящее время объявлено устаревшим.
Если старое API недостаточно хорошо для новой ОС, но необходимо поддержать работу старых приложений, имеет смысл содержать старую ОС внутри ВМ вместе с приложениями для неё. Это было использовано в случае Windows для исполнения приложений DOS, и в Mac OS X для старых Mac OS приложений.
Если интерфейс старой ОС сравнительно небольшой или детальное повторение семантики вызвов необязательно, эмуляция API может оказаться лучшим решением: перехват системных вызовов старого приложения с отображением их на новые. Это было использовано для обеспечения работы DOS-приложений в Windows NT; при этом уровень совместимости был средний.
Неожиданно увидеть, насколько разными способами выполнялась миграция для всех рассмотренных ОС, ни один подход не повторял полностью другой. Причина этого, возможно, состоит в том, что условия для перехода в каждом случае были немного различные, и много времени уходило на разработку наилучшего решения для каждого конкретного случая.
Но есть и общие закономерности. С течением времени ОС становились всё более современными и менее аляповатыми; кроме того, миграции проходили через множество маленьких шагов, избегая больших прыжков. Современные ОС, Windows NT и Mac OS X, могут быть портированы на новые архитектуры относительно легко, эмуляторы помогают исполнять старые приложения, а трансляция системных вызовов может быть использована для отображения их на родные для ОС вызовы. Благодаря абстракциям, используемым вы системах, ОС может быть портирована на новую архитектуру или разрядность по шагам, и часть её может оставаться в старом окружении, а часть быть переведена на новое. Эти же абстракции позволяют разработчикам подменять целые подсистемы и переделывать части ОС без сильного эффекта на пользователей. Эти процессы становятся всё более отлаженными и отработанными — и, увы, менее увлекательными.
К сожалению, некоторые ссылки оригинального поста ведут в никуда, и найти документы мне не удалось.
1. Pat Villani. FreeDOS Kernel: An MS-DOS Emulator for Platform Independence & Embedded System Development
2. Битая ссылка на документацию PowerPC от Apple
3. Brian Maas. Palm OS 5 ARM Programming
4. PNOLet Samples
Производители операционных систем сталкиваются с этой проблемой один или два раза в десятилетие: им необходимо перевести свою пользовательскую базу со старой операционной системы на их сильно отличающуюся новую ОС, или им требуется перейти с одной архитектуры ЦПУ на другую с сохранением возможности запуска старых приложений без модификаций, а также помочь сторонним разработчикам портировать свои приложения на новую ОС.
Давайте рассмотрим, как это происходило в последние 30 лет, на примерах MS DOS/Windows, Apple Macintosh, Amiga OS и Palm OS.
От CP/M к PC-DOS/MS-DOS
CP/M — 8-битная ОС от фирмы Digital Research, работавшая на всех вариантах систем на основе Intel 8080. 86-DOS от Seattle Computer Products, позже ставшая известной как MS-DOS (именуемая также PC-DOS на машинах от IBM), была клоном CP/M для более позднего Intel 8086; аналогичная ситуация была и с CP/M-86 от самой DR (эта ОС позже была названа DR-DOS).
Не являясь совместимым на двоичном уровне, Intel 8086 был «совместим на уровне ассемблера» с 8080. Это означало, что было легко сконвертировать 8-битную программу, написанную на ассемблере для 8080 или Z80, в программу на ассемблере для 8086, так как эти две архитектуры были очень похожи (обратно-совместимая модель памяти, легко отображаемые друг на друга наборы регистров), различались лишь машинные кодировки команд.
Так как MS-DOS использовала те же самые соглашения ABI и карту памяти, эта ОС была совместима на уровне исходных кодов с CP/M. На системах CP/M, имеющих доступ максимум к 64 кБайт памяти, диапазон адресов с 0x0000 по 0x0100 (нулевая страница) была зарезервирована под нужды ОС и содержала, кроме прочего, аргументы командной строки. Исполняемое приложение располагалось, начиная с адреса 0x0100 и выше, а ОС размещалась в верхней части памяти; стек приложения рос вниз, начинаясь чуть ниже начала ОС. Модель памяти 8086 разбивает память на (перекрывающиеся) непрерывные сегменты длиной в 64 кбайт, так что каждый из этих сегментов фактически достаточен для виртуальной машины 8080. Поэтому исполняемые файлы .COM MS-DOS имеют размер до 64 кбайт и загружаются в память, начиная с адреса 0x100. Диапазон 0x0 — 0x100 в MS-DOS именуется Program Segment Prefix и он очень похож на нулевую страницу CP/M. Из-за высокой степени совместимости этих процессоров и их ABI перенос программы с CP/M в DOS происходил относительно безболезненно. Однако, такой порт мог использовать максимум 64 кбайт памяти (примеры — Wordstar 3.0 1982 года). Но это позволяло иметь единую кодовую приложения базу для обеих ОС с помощью лишь нескольких макроопределений и двух различных программ-ассемблеров.
MS-DOS 2.0 принесла с собой новые, более мощные соглашения API (хэндлы для файлов вместо структур FСB, поддиректории, формат .EXE) и перевела большую часть API от CP/M в статус устаревших. Но DOS сохраняла совместимость с CP/M вплоть до последней версии.
От CP/M к PC-DOS/MS-DOS | |
Изменение | Новый ЦПУ Новая кодовая база ОС |
Исполнение новых приложений | Прямое |
Исполнение старых приложений (без модификации) |
Не поддерживается |
Исполнение старых драйверов | Не поддерживается |
Средства для портирования приложений | Высокая степень совместимости исходных кодов и ABI |
От DOS к Windows
Изначально Microsoft Windows была спроектирована как графическая оболочка поверх MS-DOS. Вся работа с устройствами и файловой системой происходила через вызовы DOS API. Поэтому все драйверы MS-DOS исполнялись напрямую, и Windows могла использовать их. Приложения DOS всё ещё могли запускаться после выхода из Windows в чистый DOS.
В Windows/386 2.1 модель была изменена. Это уже было настоящее ядро ОС, исполнявшее множество виртуальных машин в режиме virtual 8086 (V86) бок о бок; одна была выделена для сессии ОС MS-DOS, и по одной выделялось для каждого приложения Windows. Виртуальная машина DOS использовалась Windows для обращения к драйверам устройств и файловой системы, так что это был практически слой совместимости драйверов, исполняемый внутри ВМ. Пользователь мог запустить любое число дополнительных ВМ DOS для работы с DOS-приложениями; и каждая при этом содержала свою копию DOS. Windows перехватывала обращения с памяти экрана, а также некоторые системные вызовы, и перенаправляла их с графический драйвер Windows или в первую «главную» ВМ DOS.
Windows 3.x начала использовать родные драйвера для Windows, которые заменили обращения внутрь DOS ВМ, а также начала перенаправлять некоторые вызовы DOS к устройствам внутрь Windows. Стандартная установка Windows 95 уже совсем не использовала обращения к DOS ВМ для нужд работы с драйверами и файловой системой, однако при необходимости такой режим мог ещё мог быть использован.
DOS являлась не только средой обеспечения совместимости для старых драйверов и приложений, она также предоставляла командную строку Windows, поэтому, когда в Windows 95 появилась поддержка длинных имён файлов, пришлось перехватывать вызовы API DOS для предоставления новой функциональности для утилит командной строки.
От MS-DOS к Windows | |
Изменение | Новая ОС |
Исполнение новых приложений | Прямое |
Исполнение старых приложений | Виртуальная машина со старой ОС |
Исполнение старых драйверов | Виртуальная машина со старой ОС |
Средства для портирования приложений | Не предусмотрено |
От DOS к Windows NT
Системы семейства Windows NT не базировались на DOS, тем не менее с самой первой версии они позволяли исполнять приложения MS-DOS. Как и в случае не-NT варианта Windows, в таком случае DOS-программа работала в V86-режиме процессора. Однако, вместо использования полной копии MS-DOS внутри ВМ, NT помещала в неё только код приложения, перехватывала все системные вызовы и обращения к устройствам и оборачивала их в вызовы NT API. Фактически, этот способ не является чистой ВМ: режим V86 использовался только для создания необходимой для поддержки DOS-приложений модели памяти.
Есть широко распространённое заблуждение, что командная строка Windows NT — это «оболочка DOS». На самом деле интерпретатор и вспомогательные утилиты были родными NT-приложениями, и подсистема NTDVM (виртуальная машина DOS) вообще не стартовала до тех пор, пока действительно не возникала необходимость в запуске DOS-программы из командной строки.
От DOS к Windows NT | |
Изменение | Новая ОС |
Исполнение новых приложений | Прямое |
Исполнение старых приложений | Совместимые API |
Исполнение старых драйверов | Не поддерживаются |
Средства для портирования приложений | Не предусмотрены |
От Windows 3.1 (Win16) к Windows 95 (Win32)
Начиная с релиза Windows NT 3.1 в 1993 году стало понятно, что она в конце концов заменит классическую Windows. Несмотря на то, что NT имела одинаковый с Win16 пользовательский интерфейс и хорошую совместимость с DOS, каждая выходившая новая версия обычно требовала для работы довольно мощный компьютер. Поэтому миграция с Windows на Windows NT проходила обратным образом: каждая версия Windows становилась всё больше похожей на NT до тех пор, пока они не стали достаточно похожими. К тому времени даже слабые компьютеры той эпохи стали достаточно мощными для работы под NT.
Большим шагом по сближению Windows и Windows NT стала поддержка родного для NT Win32 API. Первым этапом было бесплатное обновление «Win32S» к Windows 3.1, которое предоставляло подмножество (отсюда «S» — subset — в названии) Win32 API на классической Windows. Win32S расширило ядро Windows возможностью создания 32-битных адресных пространств для всех 32-битных приложений (в NT для каждого приложения создавалось собственное пространство). Оно также сделало доступным версии некоторых библиотек из NT (например, RICHED32.DLL), а также 32-битные DLL, принимавшие вызовы из низкоуровневых Win32API вызовов (подсистемы «GDI» и «USER»), автоматически траслировались в вызовы подсистемы Win16 («thunking»).
В Windows 95 эта функциональность была доступна по умолчанию. Она также использовала изолированные адресные пространства для 32-битных приложений, поддерживала большее подмножество Win32 API. Некоторые базовые приложения стали 32-битными (например, Проводник), однако значительная часть базовой системы всё ещё была 16-битной. С появлением Windows 95 большинство разработчиков переключились на создание 32-битных приложений, что позволяло также компилировать их и на системах Windows NT.
От Windows 3.1 (Win16) к Windows 95 (Win32) | |
Изменение | Новый режим процессора и адресации |
Исполнение новых приложений | Прослойки совместимости |
Исполнение старых приложений | Прямое |
Исполнение старых драйверов | Прямое |
Методы портирования | Высокая совместимость исходных кодов |
От Windows 9x к Windows NT
Второй шаг по миграции с 16-битной Windows на Windows NT произошёл при переходе от Windows ME на Windows XP (NT-семейство) в 2001. Windows NT (2000, XP...) была полностью 32-битной ОС с Win32 API, но также позволяла исполнять 16-битные приложения Windows через трансляцию вызовов Win16 в Win32 (thunking).
Модель драйверов Windows NT 3.1/3.5/4.0 (Windows NT Driver Model) отличалась от подхода классической Windows (VxD), поэтому Windows 98 (наследник Windows 95) и Windows 2000 (наследник Windows 4.0) обе поддерживали новую «Модель драйверов Windows». Один и тот же драйвер теперь мог работать в обеих ОС, но каждая при этом продолжала поддерживать и свой оригинальный режим для старых драйверов.
Когда Microsoft перевела домашних пользователей на системы NT, большинство приложений, игр и драйверов заработали на Windows XP. Единственное, что пришлось переписывать — это системные утилиты.
От Windows 9x к Windows NT | |
Изменение | Новая ОС |
Исполнение новых приложений | Прямое |
Исполнение старых приложений | Win16: трансляция API Win32: прямое |
Исполнение старых драйверов | Поддержка API от старой ОС |
Методы портирования приложений | Высокий уровень совместимости исходного кода Поддержка API от старой ОС |
От Windows i386 (Win32) к Windows x64/x86_64 (Win64)
Переход от 32-битных Windows к 64-битным происходит в настоящее время. Windows XP была первой ОС от Microsoft, доступной для архитектуры Intel 64/AMD64, все последующие системы (Vista, 7, 8, 10) доступны в вариантах для 32 и 64 бит. В 64-битных редакциях ядро работает в 64-битном режиме, как и все системные библиотеки и большинство приложений. API для 32 бит поддерживается через подсистему WOW64 (Windows-on-Windows 64). 32-битное приложение линкуется с 32-битными библиотеками, однако низкоуровневые вызовы API транслируются через WOW64 в аналоги для 64-битных DLL.
Так как драйверы исполняются в том же адресном пространстве, что и ядро, было трудно обеспечить поддержку работы 32-битных драйверов, поэтому её нет. Также была прекращена поддержка приложений DOS и Win16 в 64-битных вариантах ОС.
От Windows i386 (Win32) к Windows x64/x86_64 (Win64) | |
Изменение | Новый режим процессора и адресации |
Исполнение новых приложений | Прямая |
Исполнение старых приложений | Трансляция вызовов |
Исполнение старых драйверов | Не поддерживается |
Методы портирования приложений | Высокий уровень совместимости исходных кодов |
От Apple Macintosh для 68K к Macintosh на PowerPC
Apple перевела свои компьютеры с процессоров Motorola 68k на Motorola/IBM PowerPC в период с 1994 по 1996 год. Так как операционная система для Macintosh, System 7, была в основном написана на ассемблере для 68k, она не могла быть легко сконвертирована в ОС для PowerPC. Вместо этого, большая часть её исполнялась через симуляцию. Новое «наноядро» получало и обрабатывало прерывания и выполняло минимальное управление памятью, абстрагируя особенности PowerPC, а интегрированный эмулятор 68k исполнял старую ОС, которая была модифицирована для связывания с наноядром. Таким образом, System 7.1.2 для PowerPC была фактически паравиртуализована и исполнялась поверх очень тонкой прослойки гипервизора.
Первая версия Mac OS для PowerPC исполняла большую часть системного кода внутри эмулятора для 68k, включая драйверы. Часть кода, критичного для обеспечения производительности, была переписана на прямое исполнение. Загрузчик исполняемых файлов мог детектировать программы с PowerPC и мог исполнять их напрямую. Большая часть коммуникаций с ОС всё равно шла через эмулятор. В более поздних версиях Mac OS всё большая доля кода переписывалась с 68k на PowerPC.
С Macintosh для 68k на Macintosh для PowerPC | |
Изменение | Новый процессор |
Исполнение новых приложений | Трансляция вызовов |
Исполнение старых приложений | Паравиртуализованная старая ОС внутри эмулятора |
Исполнение старых драйверов | Паравиртуализованная старая ОС внутри эмулятора |
Метод портирования приложений | Высокий уровень совместимости исходных кодов |
От Классической Mac OS на Mac OS X
Аналогично тому, как Microsoft перевела пользователей с Windows на Windows NT, Apple перешла с классической Mac OS на Mac OS X. Тогда как классическая Mac OS была аляповатой (hackish) ОС с кооперативной многозадачностью, без защиты памяти, и при этом всё ещё исполняла часть системного кода внутри эмулятора 68k, Mac OS X была основана на NEXTSTEP, современной UNIX-подобной ОС с полностью отличающимся API.
Когда в Apple решили провести миграцию, они портировали системные библиотеки классической Mac OS («Toolbox»), при этом убрав вызовы, которые не могли быть поддержаны на современной ОС, заменив их альтернативами, и назвали новое API «Carbon». То же самое API стало доступно в классической Mac OS 8.1 в 1998, так что разработчики смогли обновить свои приложения для работы в Mac OS X, сохранив при этом совместимость с классической Mac OS. Когда Mac OS X стала публично доступна в 2004 году, «карбонизированные» к этому моменту приложения могли работать на обеих ОС без перекомпиляции. Это напоминает подход Microsoft по приближению классической Windows к Windows NT.
Но так как нельзя было ожидать, что все приложения будут сконвертированы в «карбонизированную» форму к моменту появления Mac OS X, новая ОС также содержала виртуальную машину «Classic» или «Blue Box», в которой немодифицированная Mac OS 9 исполнялась и могла поддерживать произвольное число старых приложений. Запросы к сети и файловой системе внутри ВМ перехватывались и перенаправлялись в хозяйскую ОС, а интеграция с оконным менеджером позволяла двум средам рабочего стола выглядеть практически бесшовно.
От Classic Mac OS на Mac OS X | |
Изменение | Новая ОС |
Исполнение новых приложений | Прямое |
Исполнения старых приложений | Classic: ВМ со старой ОС Carbon: Общее API для обеих систем |
Исполнение старых драйверов | Виртуальная машина для старой ОС |
Метод портирования приложений | Общее API для обеих систем |
Переход Mac OS X с PowerPC на Intel IA-32
В 2005 году Apple анонсировала второй переход с одной архитектуры ЦПУ на другую, на этот раз с PowerPC на Intel IA-32. Так как эта ОС была в основном написана на C и Objective C, она могла легко быть портирована на IA-32. В Apple утверждают, что они всегда поддерживали вариант ОС для IA-32, начиная с самого первого релиза.
Для того, чтобы исполнять старые приложения, ещё не портированные на IA-32, Apple включила эмулятор Rosetta в состав новой ОС. В этот раз он не был тесно интегрирован с ядром, как это было в случае перехода с 68k на PowerPC. Ядро предоставляло лишь возможность запуска внешнего рекомпилятора для приложения всякий раз, когда оно оказывалось предназначенным для PowerPC. Rosetta транслировала весь код приложения и код библиотек и предоставляла интерфейс для взаимодействия с ядром ОС.
Mac OS X с PowerPC на Intel IA-32 |
|
Изменение | Новая архитектура ЦПУ |
Исполнение новых приложений | Прямое |
Исполнение старых приложений | Эмулятор режима пользователя |
Исполнение старых драйверов | Не поддерживается |
Метод портирования приложений | Высокий уровень переносимости исходного кода |
Mac OS X 32 бита на Mac OS X 64 бита
Следующий переход для Apple заключался в миграции с 32-битной архитектуры Intel на её 64-битный вариант. Он произошёл в Mac OS X 10.4 в 2006 году. Хотя ОС целиком могла бы быть портирована на 64 бита, как это было сделано в случае Windows, Apple решила использовать подход, более похожий на переход на Windows 95: ядро осталось 32-битным, но получило поддержку 64-битных приложений. Все системные приложения и драйверы остались 32-битными, но некоторые библиотеки также получили 64-битные варианты. Таким образом, 64-битное приложение линковалось с 64-битными библиотеками и выполняло 64-битные системные вызовы, которые при этом конвертировались с 32-битные внутри ядра.
Mac OS X 10.5 начала поставлять все библиотеки в 64-битном варианте, ядро осталось 32-битным. В версии 10.6 ядро было переведено на 64 бита, и использование 64-битных драйверов стало обязательным.
Mac OS X 32 бита на Mac OS X 64 бита |
|
Изменение | Новый ЦПУ и режим адресации |
Исполнение новых программ | Трансляция вызовов |
Исполнение старых программ | Прямое |
Исполнение старых драйверов | Прямое |
Метод портирования приложений | Carbon: Не поддерживается Cocoa: Совместимость исходных кодов |
AmigaOS с 68k на PowerPC
Платформа Amiga использовала одну и ту же ОС для ЦПУ 68k в эпоху Commodore с 1985 по 1994 год. Однако, с 1997 года для неё существовал апгрейд на плату с ЦПУ архитектуры PowerPC. Эта операционная система не предоставляла свой исходный код для сторонних, поэтому её невозможно было портировать на PowerPC. По этой причина сама Amiga OS продолжала работать на выделенном чипе 68k, и расширенный загрузчик программ детектировал PowerPC код и передавал его на второй ЦПУ. Все системные вызовы маршрутизировались через библиотечную прослойку обратно на 68k.
AmigaOS 4 (2006 год) является настоящим портом AmigaOS на PowerPC. Этот переход потребовал больших усилий, так как много исходного кода ОС пришлось сперва конвертировать c языка BCPL в язык Си. Поддержка приложений для 68k сделана через эмуляцию двоичного кода и пробросу API вызовов.
AmigaOS c 68k на PowerPC (версии 3.x) | |
Изменение | Новый ЦПУ |
Исполнение новых приложений | Трансляция системных вызовов (на новом ЦПУ) |
Исполнение старых приложений | Прямое (на старом ЦПУ) |
Исполнение старых драйверов | Прямое |
Метод портирования приложений | Высокий уровень совместимости исходных кодов |
AmigaOS с68k на PowerPC (версия 4.0) | |
Изменение | Новый ЦПУ |
Исполнение новых приложений | Прямое |
Исполнение старых приложений | Эмулятор уровня пользователя |
Исполнение старых драйверов | Не поддерживается |
Метод портирования приложений | Высокий уровень совместимости исходных кодов |
Palm OS с 68k на ARM
Palm перешёл с процессоров 68k на ARM в версии Palm OS 5 в 2002 году. ОС была портирована на ARM, и эмулятор для 68k «Palm Application Compatibility Environment» («PACE») был включён в её состав для исполнения старых приложений. Однако, Palm не рекомендовала разработчиками переключаться на написание приложений для ARM и даже не предоставляла среды для исполнения таких приложений под управлением ОС. Утверждалось, что большинство приложений для Palm OS большую часть времени проводили в (уже оптимизированном «родном») коде операционной системы, и поэтому они не получили бы ощутимого выигрыша в скорости при портировании.
Однако для приложений, интенсивно использующих ЦПУ или содержащих код для сжатия или шифрования данных, Palm предоставила метод исполнения небольших кусочков ARM-кода внутри приложения для 68k. Подобные ARMlet'ы (позже названные PNOlet'ы — «Palm Native Object») можно было вызывать из кода для 68k. Они предоставляли минимальный интерфейс передачи данных (одно целое число на вход и одно — на выход). Разработчикам самим приходилось заботиться о передаче больших структур в памяти и решать проблемы порядка байт (endianness) и их выравнивания. Процедуры из ARM-кода не могли ни вызывать код для 68k, ни делать API вызовы операционной системы.
Привязка к архитектуре 68k (не являющейся родной для используемого железа) как основной для большинства приложений фактически означал использование виртуальной архитектуры пользовательских приложений, на манер Java или .NET. Переход на ARM был практически незаметен для сторонних разработчиков, и в теории такой подход позволял Palm вновь менять хозяйскую архитектуру при необходимости в будущем с минимальными затратами.
Palm OS с 68k на ARM | |
Изменение | Новый ЦПУ |
Исполнение новых приложений | Не поддерживается (PNOlet для процедур) |
Исполнение старых приложений | Эмуляция уровня пользователя |
Исполнение старых драйверов | Не поддерживается |
Метод портирования приложений | Не поддерживается (PNOlet для процедур) |
Выводы
Давайте ещё пробежимся по рассмотренным переходам между ОС и ЦПУ и способам, как различные вендоры подходили к задаче миграции.
Разрядность (ширина адресного пространства)
Переход к новому режиму процессора — самый простой для ОС, так как старые приложения могут продолжать исполняться напрямую, а вызовы API могут быть транслированы. При этом сама ОС может или продолжить использовать старый режим, конвертируя системные вызовы из новых приложений в старый формат, или же перейти на новую разрядность и конвертировать вызовы старых приложений. Также есть два способа перехватывать сами вызовы. Можно перехватывать высокоуровневые вызовы API (такие как создание GUI-окна), но это сложно выполнить, так как таких вызовов много, и сложно написать корректный конвертор для столь большого числа функций.
Альтернативно, ОС может конвертировать низкоуровневые системные вызовы. В этом случае перехватываемый интерфейс очень узкий. Но, так как старые приложения линкуются со старыми библиотеками, а новые — с новыми библиотеками, их хранение и использование потребляет в два раза больше места (памяти) в случаях, когда пользователь исполняет старые и новые приложения одновременно.
Новый режим процессора или разрядность |
||||
ОС | Старый режим | Новый режим | Направление трансляции вызовов |
Уровень перехвата вызовов |
Windows | 16 бит | 32 бит | новые к старому | библиотеки |
Windows NT | 32 бит | 64 бит | старые к новому | ядро |
Mac OS X | 32 бит | 64 бит | новые к старому | ядро |
В случае перехода с 16 до 32 бит в Windows, ОС осталась 16-битной, а вызовы конвертировались из 32-битного формата в 16-битное API. Когда Windows NT переводили с 32 до 64 бит, вся ОС стала 64-битной, а старые системные вызовы конвертировались в новые. Тот же переход был выполнен иначе в случае Mac OS X: ОС осталась 32-битной, а 64-битные системные вызовы транслировались на уровне ядра.
Подходы, использованные для Windows NT и Mac OS X похожи, так как в обоих случаях 32-битные приложения продолжили использовать 32-битные библиотеки, а 64-битные — новые 64-битные библиотеки. Различие были в режиме нового ядра. В случае Windows возникало преимущество в виде возможности использования более 4 Гбайт памяти в режиме ядра и некоторое ускорение от использования нового 64-битного набора регистров. В случае Mac OS X, осталась возможность продолжать использовать старые 32-битные драйвера без модификаций. И да, вторым шагом Mac OS X позже полностью перешла на 64-битное ядро и драйверы.
Процессор
Сменить используемый процессор сложнее, так как на новой архитектуре нельзя больше исполнять старый машинный код. Некоторые ОС не так-то просто было перенести.
Новый процессор | ||||
ОС | Старый ЦПУ | Новый ЦПУ | Метод запуска старых приложений | Уровень перехвата вызовов |
CP/M, DOS | 8080/Z80 | 8086 | Перекомпиляция | - |
Macintosh | 68K | PowerPC | Исполнение ОС и приложения внутри эмуляции | - |
Mac OS X | PowerPC | i386 | Эмуляция уровня пользователя | ядро |
Amiga | 68K | PowerPC | Двойной ЦПУ и трансляция вызовов | библиотеки |
Palm | 68K | ARM | Эмуляция уровня пользователя | библиотеки |
Mac OS X для Intel и Palm OS для ARM были написаны в достаточно платформенно-независимой манере, чтобы быть переносимой на новую архитектуру. Обе включали в себя рекомпиляторы, которые исполняли старый код. Это наиболее простой подход. Amiga OS не могла быть портирована таким образом, так как её исходный код не был доступен. Поэтому система включала оба процессора — оригинальная ОС и её код исполнялся на старом процессоре, а на новом процессоре работал новый код, отправляя системные вызовы на старый ЦПУ.
Для классической Macintosh (с 68k на PowerPC) исходный код был доступен, но всё равно не мог быть легко изменён. Поэтому переход был выполнен аналогично ситуации с Amiga, но на одном ЦПУ. Большая часть ОС исполнялась внутри эмулятора, новые приложения запускались напрямую, вызывая системные вызовы обратно из эмулятора.
MS-DOS был реализацией старой ОС, выполненный другой компанией. Она не поддерживала исполнение старого двоичного кода. Вместо этого, разработчикам предлагалось перекомпилировать свои приложения.
Операционные системы
Переход на новую ОС с сохранением всех пользователей и программ — самый сложный из рассматриваемых.
Новая ОС | ||
Старая ОС | Новая ОС | Метод исполнения старых приложений |
CP/M | DOS | Совместимое API |
DOS | Windows | Виртуальная машина со старой ОС |
DOS | Windows NT | Эмуляция API |
Windows 9X | Windows NT | Совместимый API |
Mac OS | Mac OS X | Classic: виртуальная машина со старой ОС Carbon: совместимое API |
Выбираемый подход зависит от планов поддержки старого API от старой ОС. Если API было достаточно хорошо, чтобы его стоило поддерживать в новой ОС, новая ОС должна просто поддерживать то же самое API. Это выполнялось для случаев переходов от CP/M к DOS и от Windows 9x к Windows NT. В каком-то смысле это выполнялось для перехода с классической Mac OS на Mac OS X, но в этом случае Carbon не являлся главным API новой ОС, а лишь одним из трёх поддерживаемых (Carbon, Cocoa, Java). Практически всё кроме Cocoa, в настоящее время объявлено устаревшим.
Если старое API недостаточно хорошо для новой ОС, но необходимо поддержать работу старых приложений, имеет смысл содержать старую ОС внутри ВМ вместе с приложениями для неё. Это было использовано в случае Windows для исполнения приложений DOS, и в Mac OS X для старых Mac OS приложений.
Если интерфейс старой ОС сравнительно небольшой или детальное повторение семантики вызвов необязательно, эмуляция API может оказаться лучшим решением: перехват системных вызовов старого приложения с отображением их на новые. Это было использовано для обеспечения работы DOS-приложений в Windows NT; при этом уровень совместимости был средний.
Заключение
Неожиданно увидеть, насколько разными способами выполнялась миграция для всех рассмотренных ОС, ни один подход не повторял полностью другой. Причина этого, возможно, состоит в том, что условия для перехода в каждом случае были немного различные, и много времени уходило на разработку наилучшего решения для каждого конкретного случая.
Но есть и общие закономерности. С течением времени ОС становились всё более современными и менее аляповатыми; кроме того, миграции проходили через множество маленьких шагов, избегая больших прыжков. Современные ОС, Windows NT и Mac OS X, могут быть портированы на новые архитектуры относительно легко, эмуляторы помогают исполнять старые приложения, а трансляция системных вызовов может быть использована для отображения их на родные для ОС вызовы. Благодаря абстракциям, используемым вы системах, ОС может быть портирована на новую архитектуру или разрядность по шагам, и часть её может оставаться в старом окружении, а часть быть переведена на новое. Эти же абстракции позволяют разработчикам подменять целые подсистемы и переделывать части ОС без сильного эффекта на пользователей. Эти процессы становятся всё более отлаженными и отработанными — и, увы, менее увлекательными.
Литература
К сожалению, некоторые ссылки оригинального поста ведут в никуда, и найти документы мне не удалось.
1. Pat Villani. FreeDOS Kernel: An MS-DOS Emulator for Platform Independence & Embedded System Development
2. Битая ссылка на документацию PowerPC от Apple
3. Brian Maas. Palm OS 5 ARM Programming
4. PNOLet Samples