Vikont Home page
НОВОСТИ

20.01.05
Открылся сайт.


17.01.05
Ура! Сессия сдана досрочно и вполне успешно: матан 4, линейка 5, инфа 4; автоматы: история 5, ПАСИ 4.




СТАТЬИ

Подсистема управления процессами в различных реализациях ОС UNIX.

Сети. Характеристика ЛВС города Химки.

Сопоставление языков C и Pascal

...другие
HOME > MY WORK >

Подсистема управления процессами в различных реализациях OC UNIX

Определение операционной системы.

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

Расширенная машина – это совокупность абстрактных интерфейсов, которые предоставляет ОС для удобной и простой работы с реальной аппаратурой машины. Например, ОС скрывает от программиста все реалии аппаратуры и предоставляет возможность легкого просмотра указанных файлов, чтения или записи. Данный абстрактный интерфейс, реализующий такие понятия как файл, каталог, а так же основные операции над ними, называется файловой системой.

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

  • планирование ресурса - то есть определение, кому, когда, а для делимых ресурсов и в каком количестве, необходимо выделить данный ресурс;
  • отслеживание состояния ресурса - то есть поддержание оперативной информации о том, занят или не занят ресурс, а для делимых ресурсов - какое количество ресурса уже распределено, а какое свободно.

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

В своем реферате я имею цель рассмотреть различные аспекты подсистемы управления процессами в современных реализациях ОС UNIX.

Общие концепции подсистемы управления процессами.

Процесс (или задача) – это абстракция, описывающая выполняющуюся программу. Для операционной системы процесс представляет собой единицу работы, заявку на потребление системных ресурсов. Подсистема управления процессами планирует выполнение процессов, то есть распределяет процессорное время между несколькими одновременно существующими в системе процессами, а также занимается созданием и уничтожением процессов, обеспечивает процессы необходимыми системными ресурсами, поддерживает взаимодействие между процессами.

Состояние процессов

В многозадачной системе процесс может находиться в одном из трех основных состояний:

Выполнение – активное состояние, в котором код процесса выполняется на процессоре, а сам процесс обладает всеми необходимыми для работы ресурсами;

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

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

В зависимости от алгоритма планирования ОС, каждый процесс в течении своего жизненного цикла переходит из одного состояния в другое. В данный момент времени только один процесс в системе может находиться в состоянии выполнения, в состоянии готовности и ожидания могут находиться несколько процессов, образуя при этом очереди ожидающих и готовых процессов соответственно. Свой жизненный цикл каждый процесс начинает с состояния готовности. Затем этот процесс принимает состояние выполнения, выйти из которого он может двумя различными способами: 1) процесс переходит в состояние ожидания какого-либо события 2) процесс насильно вытесняется с центрального процессора, вследствие исчерпания доступного процессу кванта отведенного времени. Во втором случае процесс принимает состояние готовности. В это же состояние процесс переходит из состояния ожидания, после того как ожидаемое событие произойдет.

Контекст и дескриптор процесса

Для полноценного управления процессами, операционная система нуждается в специальных структурах данных, описывающих процесс и его операционную среду. Состояние операционной среды отображается состоянием регистров и программного счетчика, режимом работы процессора, указателями на открытые файлы, информацией о незавершенных операциях ввода-вывода, кодами ошибок выполняемых данным процессом системных вызовов и т.д. Эта информация называется контекстом процесса. Так же данную структуру иногда называют пространством процесса.

Кроме этого, операционной системе для реализации планирования процессов требуется дополнительная информация: идентификатор процесса, состояние процесса, данные о степени привилегированности процесса, место нахождения кодового сегмента и другая информация. ОС UNIX информацию такого рода, используемую ОС для планирования процессов, называют дескриптором процесса. Дескрипторы процессов объединяются в таблицы процессов.

Код программы начнет свое выполнение только после создания процесса. Создать процесс означает:

  • создать информационные структуры, описывающие данный процесс, то есть его дескриптор и контекст;
  • включить дескриптор нового процесса в очередь готовых процессов;
  • загрузить кодовый сегмент процесса в оперативную память или в область свопинга1.

Алгоритмы планирования процессов

Планирование реализует в себе решения нескольких важных для процессорной среды задач. Во-первых, это определение момента времени для смены выполняемого процесса. Во-вторых, это выбор процесса на выполнение из очереди готовых процессов, и , наконец, это задача переключения контекстов "старого" и "нового" процессов.

Первые две задачи решаются программными средствами, а последняя в значительной степени аппаратно.

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

  • алгоритмы, основанные на квантовании;
  • алгоритмы, основанные на приоритетах.

В соответствии с алгоритмами, основанными на квантовании, смена активного процесса происходит, если:

- процесс завершился и покинул систему,

- произошла ошибка,

- процесс перешел в состояние ожидание,

- исчерпан квант процессорного времени, отведенный данному процессу.

Процесс, который исчерпал свой квант, переводится в состояние готовность и ожидает, когда ему будет предоставлен новый квант процессорного времени, а на выполнение в соответствии с определенным правилом выбирается новый процесс из очереди готовых. Таким образом, ни один процесс не занимает процессор надолго, поэтому квантование широко используется в системах разделения времени. Граф состояний процесса, изображенный на рисунке 2.1, соответствует алгоритму планирования, основанному на квантовании.

Кванты, выделяемые процессам, могут быть одинаковыми для всех процессов или различными. Кванты, выделяемые одному процессу, могут быть фиксированной величины или изменяться в разные периоды жизни процесса. Процессы, которые не полностью использовали выделенный им квант (например, из-за ухода на выполнение операций ввода-вывода), могут получить или не получить компенсацию в виде привилегий при последующем обслуживании. По разному может быть организована очередь готовых процессов: циклически, по правилу "первый пришел - первый обслужился" (FIFO) или по правилу "последний пришел - первый обслужился" (LIFO).

Другая группа алгоритмов планирования, использует понятие “приоритет”. Приоритет - это число, характеризующее степень привилегированности процесса при использовании ресурсов вычислительной машины, в частности, процессорного времени: чем выше приоритет, тем выше привилегии. Приоритет может выражаться целым и дробным числом, иметь положительное и отрицательное значение. Чем выше приоритет процесса тем меньше времени он будет проводит в очередях на выполнение. Приоритет может назначаться процессу администратором, либо вычисляться самой ОС по определенным правилам. Приоритеты могут оставаться постоянными при всей жизни процесса, а могут изменяться. В последнем случае имеют место динамические приоритеты.

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

В обоих случаях выбор процесса на выполнение из очереди готовых осуществляется одинаково: выбирается процесс, имеющий наивысший приоритет. По разному решается проблема определения момента смены активного процесса. В системах с относительными приоритетами активный процесс выполняется до тех пор, пока он сам не покинет процессор, перейдя в состояние ожидание (или же произойдет ошибка, или процесс завершится). В системах с абсолютными приоритетами выполнение активного процесса прерывается еще при одном условии: если в очереди готовых процессов появился процесс, приоритет которого выше приоритета активного процесса. В этом случае прерванный процесс переходит в состояние готовности.

Существуют два основных типа процедур планирования процессов - вытесняющие (preemptive) и невытесняющие (non-preemptive).

Non-preemptive multitasking - невытесняющая многозадачность – это такой способ планирования, при котором процесс сам решает, когда завершить свое активное состояние и перейти в режим ожидания или готовности. ОС в свою очередь выбирает для выполнения новый, готовый для выполнения, процесс.

Preemptive multitasking - вытесняющая многозадачность – это такой способ планирования, при котором планировщик сам решает, когда приостановить выполнение текущего процесса и начать выполнение другого. Процесс переключения работающих процессов не зависит от самих процессов.

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

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

Важно отметить, что при определенных условиях невытесняющая многозадачность может иметь определенные преимущества, потому что дает возможность разработчику приложений самому проектировать алгоритм планирования, наиболее подходящий для данного фиксированного набора задач. Так как разработчик сам определяет в программе момент времени отдачи управления, то при этом исключаются нерациональные прерывания программ в "неудобные" для них моменты времени. Кроме того, легко разрешаются проблемы совместного использования данных: задача во время каждой итерации использует их монопольно и уверена, что на протяжении этого периода никто другой не изменит эти данные. Существенным преимуществом non-preemptive систем является более высокая скорость переключения с задачи на задачу.

Средства синхронизации и взаимодействия процессов

Во время выполнения процессам часто приходиться взаимодействовать друг с другом. Примером может служить направление данных одним процессом другому, или ситуация, когда несколько процессов обрабатывают один файл. Во всех этих случаях возникает проблема синхронизации процессов, которая может решаться приостановкой и активизацией процессов, организацией очередей, блокированием и освобождением ресурсов.

Важным понятие синхронизации процессов является понятие критическая секция. Критическая секция – эта та часть программы, в которой осуществляется доступ к разделяемым данным. Без применения особых методов синхронизации по отношению к некоторому ресурсу со стороны процессов может возникнуть эффект гонки, при котором в критической секции в данный момент времени будет находиться более чем один процесс. Прием синхронизации, решающий данную проблему, называется взаимным исключением. Простейший способ обеспечить взаимное исключение - позволить процессу, находящемуся в критической секции, запретить все прерывания. Однако этот способ непригоден, так как опасно доверять управление системой пользовательскому процессу; он может надолго занять процессор, а при крахе процесса в критической области крах потерпит вся система, потому что прерывания никогда не будут разрешены.

Другим способом обеспечения взаимного исключения является применение блокирующих переменных. С каждым разделяемым ресурсом связывается двоичная переменная, которая принимает значение 1, если ресурс свободен (то есть ни один процесс не находится в данный момент в критической секции, связанной с данным процессом), и значение 0, если ресурс занят. Важно отметить, что операция проверки и установки блокирующей переменной должна быть неделимая (атомарная), выполняться как единое целое и не допускать разбиения на подзадачи, иначе принцип взаимного исключения может быть нарушен.

Принцип блокирующих переменных имеет один важный недостаток. При ожидании процессом освобождения занятого ресурса происходит постоянная циклическая операция проверки блокирующей переменной, что в свою очередь занимает процессорное время. Данная проблема решается при помощи аппарата событий. В разных операционных системах аппарат событий реализуется по своему, но в любом случае используются системные функции аналогичного назначения, которые условно можно назвать Wait(x) и Post(x), где x - идентификатор некоторого события. Если ресурс занят, то процесс не выполняет циклический опрос, а вызывает системную функцию Wait(D), здесь D обозначает событие, заключающееся в освобождении ресурса D. Функция Wait(D) переводит активный процесс в состояние ожидание и делает отметку в его дескрипторе о том, что процесс ожидает события D. Процесс, который в это время использует ресурс D, после выхода из критической секции выполняет системную функцию Post(D), в результате чего операционная система просматривает очередь ожидающих процессов и переводит процесс, ожидающий события D, в состояние готовность.

В 1965 году Дейкстра (E. W. Dijkstra) предложил обобщающее средство синхронизации процессов и вел два примитива. В абстрактной форме эти примитивы, обозначаемые P и V, оперируют над целыми неотрицательными переменными, называемыми семафорами. Пусть S такой семафор. Операции определяются следующим образом:

V(S) : переменная S увеличивается на 1 одним неделимым действием; выборка, инкремент и запоминание не могут быть прерваны, и к S нет доступа другим процессам во время выполнения этой операции.

P(S) : уменьшение S на 1, если это возможно. Если S=0, то невозможно уменьшить S и остаться в области целых неотрицательных значений, в этом случае процесс, вызывающий P-операцию, ждет, пока это уменьшение станет возможным. Успешная проверка и уменьшение также является неделимой операцией.

Когда семафор S может принимать только значения 0 и 1, он превращается в блокирующую переменную. Операция P заключает в себе потенциальную возможность перехода процесса, который ее выполняет, в состояние ожидания, в то время как V-операция может при некоторых обстоятельствах активизировать другой процесс, приостановленный операцией P. Отмечу, что целое значение семафора является числом сохраненных сигналов активизации, смысл которых – реализация управления состояниями процессов. Семафоры могут использоваться как для синхронизации, так и для реализации взаимного исключения. Еще раз подчеркну, что в концепции семафоров основную роль играет неделимость операций P(S) и V(S).

Взаимоблокировка

Рассмотрим классическую задачу, приводящую к проблеме взаимоблокировки.

Пусть мы имеем два процесса, выполняющиеся в режиме мультипрограммирования, один из которых пишет данные в буферный пул, а другой считывает их из буферного пула. Пусть буферный пул состоит из N буферов, каждый из которых может содержать одну запись. Процесс "писатель" должен приостанавливаться, когда все буфера оказываются занятыми, и активизироваться при освобождении хотя бы одного буфера. Напротив, процесс "читатель" приостанавливается, когда все буферы пусты, и активизируется при появлении хотя бы одной записи.

Введем два семафора: e - число пустых буферов и f - число заполненных буферов. Предположим, что запись в буфер и считывание из буфера являются критическими секциями. Введем также двоичный семафор b, используемый для обеспечения взаимного исключения. Тогда процессы могут быть описаны следующим образом:

Если переставить местами операции P(e) и P(b) в программе "писателе", то при некотором стечении обстоятельств эти два процесса могут взаимно заблокировать друг друга. Действительно, пусть "писатель" первым войдет в критическую секцию и обнаружит отсутствие свободных буферов; он начнет ждать, когда "читатель" возьмет очередную запись из буфера, но "читатель" не сможет этого сделать, так как для этого необходимо войти в критическую секцию, вход в которую заблокирован процессом "писателем". Мы получили замкнутый круг взаимоблокировки. Взаимно заблокированными могут быть сразу несколько процессов.

Существует несколько способ решения взаимоблокировок. Первый из них – программный; различные процессы, конкурирующие за одни и те же ресурсы, могут призывать эти ресурсы в одинаковом порядке. Второй подход к предотвращению тупиковых ситуаций называется динамическим и заключается в использовании определенных правил при назначении ресурсов процессам, например, ресурсы могут выделяться в определенной последовательности, общей для всех процессов.

Чтобы упростить написание программ, в 1974 году Хоар (Hore) и Бринч Хансен (Brinch Hansen) предложили примитив синхронизации более высокого уровня, называемый монитором. Монитор - это набор процедур, переменных и структур данных. Процессы могут вызывать процедуры монитора, но не имеют доступа к внутренним данным монитора. Мониторы имеют важное свойство, которое делает их полезными для достижения взаимного исключения: только один процесс может быть активным по отношению к монитору. Компилятор обрабатывает вызовы процедур монитора особым образом. Обычно, когда процесс вызывает процедуру монитора, то первые несколько инструкций этой процедуры проверяют, не активен ли какой-либо другой процесс по отношению к этому монитору. Если да, то вызывающий процесс приостанавливается, пока другой процесс не освободит монитор. Таким образом, исключение входа нескольких процессов в монитор реализуется не программистом, а компилятором, что делает ошибки менее вероятными.

Рассмотренные нами выше примитивы межпроцессорного взаимодействия – семафоры и и мониторы – имеют один существенный недостаток. Данные примитивы не подходят для реализации обмена информацией между компьютерами. Еще одна модель межпроцессорного взаимодействия, решающая данную проблему, называется передачей сообщений. Этот метод использует два примитива: send и recieve, которые скорее являются системными вызовами, чем структурными компонентами языка. Примитивы send и recieve легко реализуются в виде библиотечных процедур системного языка программирования.

send(destination, &message);

receive(source, &message);

Первый запрос посылает сообщение заданному адресату, а второй получает сообщение от указанного источника. Если сообщения нет, второй запрос блокируется до поступления либо возвращает код ошибки.

С системами передачи сообщений связано большое количество конструктивных вопросов, которых не возникает в случае семафоров и мониторов. При взаимодействии процессов на различных компьютерах, соединенных сетью, возникает опасность потери сообщения. Что бы избежать потери сообщения получатель отправляет отправителю подтверждение приема сообщения. Если отправитель не получает подтверждения через некоторое время, сообщение посылается снова.

Для системы обмена сообщениями также важен вопрос названий процессов. Необходимо однозначно определять процесс, указанный в запросах send и recieve. Особое внимание уделяется аутентификации процессов – без решения этой проблемы гарантия безопасности системы (в частности многих серверных систем) не гарантирована.

Важным вопросом является производительность всей системы передачи сообщений. Копирование сообщений, в отличие операций на семафоре, элементами битового массива или входом в монитор, происходит намного медленнее.

Передача сообщений часто используется в системах с параллельным программированием. Характерным примером системы передачи сообщений является MPI.

Потоки или нити.

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

При мультипрограммировании повышается пропускная способность системы, но отдельный процесс никогда не может быть выполнен быстрее, чем если бы он выполнялся в однопрограммном режиме (всякое разделение ресурсов замедляет работу одного из участников за счет дополнительных затрат времени на ожидание освобождения ресурса). Однако задача, решаемая в рамках одного процесса, может обладать внутренним параллелизмом, который в принципе позволяет ускорить ее решение. Например, в ходе выполнения задачи происходит обращение к внешнему устройству, и на время этой операции можно не блокировать полностью выполнение процесса, а продолжить вычисления по другой "ветви" процесса. Применяя первое определение процесса как исполняемой задачи, мы приходим к абстракции параллельно выполняющихся подзадач. Для этих целей современные ОС предлагают использовать сравнительно новый механизм многонитевой обработки (multithreading). При этом вводится новое понятие "нить" или “поток” (thread), а понятие "процесс" в значительной степени приобретает форму контейнера ресурсов. Несколько потоков, работающих параллельно в одном процессе, аналогичны нескольким процессам работающим на одном компьютере.

У всех потоков одно и тоже адресное пространство, что означает совместное использование глобальных переменных. Зашиты от несанкционированной записи, считывания и даже удаления, информации потоком в стеке другого потока нет. Но эта защита и не нужна, поскольку концепция потоков разработана для совместного использования адресного пространства процесса.

Нити имеют собственные:

  • программный счетчик,
  • стек,
  • регистры,
  • нити-потомки,
  • состояние.

Нити разделяют:

  • адресное пространство,
  • глобальные переменные,
  • открытые файлы,
  • таймеры,
  • семафоры,
  • статистическую информацию.

Как и любой обычный процесс, поток может находиться в одном из нескольких состояний: рабочем, заблокированном, готовности и завершенном. Несмотря на то что потоки часто бывают полезными, они существенно усложняют программную модель, поскольку при их использовании возникает ряд новых вопросов. Например, вопрос наследование потоков при системном вызове fork. Или блокировка процесса при вызове read с клавиатуры: если у дочернего процесса столько же потоков, что и родительского, будут ли блокированы два потока – родительский и дочерний? Примером глобальной проблемы, служит также динамическое выделение памяти в потоках, при котором возможно повторное выделение одного и того же участка памяти. Все подобные проблемы следует решать и анализировать еще на этапе разработки многопоточной программы, что значительно замедляет и усложняет ее реализацию.

Есть два основных способа размещения потоков: в пространстве пользователя и ядре.

Размещение потоков в пространстве пользователя. Ядро ОС о потоках ничего не знает и управляет обычными, однопоточными приложениями. Преимуществом такой модели, может быть реализация потоков в процессе даже в той системе, которая не поддерживает потоки.

Если управление потоками происходит в пространстве пользователя, каждому процессу необходима собственная таблица потоков для отслеживания потоков в процессе. Каждый элемент таблицы хранит в себе характеристики отдельного потока: счетчик команд, указатель вершины стека, регистры, состояние и т.д. Важно отметить, что все процедуры управления потоками в пространстве пользователя выполняются быстрее, чем системные вызовы, реализующие схему управления потоками в пространстве ядра. Это вызвано экономией времени на прерываниях. Еще одним преимуществом реализации потоков в пространстве пользователя является тот факт, что каждый процесс может иметь свой собственный алгоритм планирования.

Данная схема управления потоками не лишена и недостатков. Существует проблема реализации блокирующих системных запросов. Еще одна проблема находит параллель с описанной выше невытесняющей многозадачностью: не один другой поток не будет запущен пока текущий поток не отдаст процессор.

Реализации потоков в ядре. При размещении потоков в ядре у каждого процесса нет необходимости в наличии таблицы потоков, вместо этого существует единая таблица потоков, размещающаяся в ядре. Все процедуры работы с потоками реализованы в виде системных вызовов, что является основным недостатком управления потоками в ядре, поскольку, как упоминалось выше, цена системного вызова намного больше чем вызов простой процедуры в пространстве пользователя.

Флаг

Значение в 1

Значение в 0

CLONE_VM

Создать новый поток

Создать новый процесс

CLONE_FS

Общие рабочий каталог, каталог root и umask

Не использовать их совместно

CLONE_FILES

Общие дескрипторы файлов

Копировать дескрипторы файлов

CLONE_SIGHAND

Общая таблица обработчика сигналов

Копировать таблицу

CLONE_PID

Новый поток получает старый PID

Новый поток получает новый PID

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

Современные реализации ОС UNIX, с целью совмещения преимуществ реализации потоков на уровне ядра и на уровне пользователя, используют смешанную реализацию.

Заключение.

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

Mac Virtual PC “DOS” на основе внутриядерной поддержки Mach 3.0