Э. Таненбаум, М. ван Стеен - Распределённые системы (принципы и парадигмы) (1162619), страница 11
Текст из файла (страница 11)
Другой серьезный плюс заключается в том, что модули пользовательскогоуровня могут в принципе размещаться на разных машинах. Так, мы можем установить модуль управления файлами не на той машине, на которой он управляетслужбой каталогов. Другими словами, подход с использованием микроядра отлично подходит для переноса однопроцессорных операционных систем на распределенные компьютеры.У микроядер имеется два существенных недостатка. Во-первых, они работают иначе, чем существующие операционные системы, а попытки поменять сложившееся «статус-кво» всегда встречают активное сопротивление («если этаоперационная система подходила для моего деда — она подойдет и для меня»).Во-вторых, микроядро требует дополнительного обмена, что слегка снижаетпроизводительность.
Однако, зная как быстры современные процессоры, снижение производительности в 20 % вряд ли можно считать фатальным.Мультипроцессорные операционные системыВажным, но часто не слишком очевидным расширением однопроцессорных операционных систем является возможность поддержки нескольких процессоров,имеющих доступ к совместно используемой памяти. Концептуально это расширение несложно. Все структуры данных, необходимые операционной системе дляподдержки аппаратуры, включая поддержку нескольких процессоров, размещаются в памяти.
Основная разница заключается в том, что теперь эти данные доступны нескольким процессорам и должны быть защищены от параллельного доступа для обеспечения их целостности.Однако многие операционные системы, особенно предназначенные для персональных компьютеров и рабочих станций, не могут с легкостью поддерживатьнесколько процессоров. Основная причина такого поведения состоит в том, чтоони были разработаны как монолитные программы, которые могут выполнятьсятолько в одном потоке управления. Адаптация таких операционных систем под1.4.
Концепции программных решений49мультипроцессорные означает повторное проектирование и новую реализациювсего ядра. Современные операционные системы изначально разрабатываютсяс учетом возможности работы в мультипроцессорных системах.Многопроцессорные операционные системы нацелены на поддержание высокой производительности конфигураций с несколькими процессорами. Основнаяих задача — обеспечить прозрачность числа процессоров для приложения. Сделать это достаточно легко, поскольку сообщение между различными приложениями или их частями требует тех же примитршов, что и в многозадачных однопроцессорных операционных системах.
Идея состоит в том, что все сообщениепроисходит путем работы с данными в специальной совместно используемой области данных, и все что нам нужно — это защитить данные от одновременногодоступа к ним. Защита осуществляется посредством примитивов синхронизации. Два наиболее важных (и эквивалентных) примитива — это семафоры и мониторы.Семафор {semaphore) может быть представлен в виде целого числа, поддерживающего две операции: up (увеличить) и down (уменьшить). При уменьшениисначала проверяется, превышает ли значение семафора 0. Если это так, его значение уменьшается и выполнение процесса продолжается. Если же значение семафора нулевое, вызывающий процесс блокируется.
Оператор увеличения совершает противоположное действие. Сначала он проверяет все заблокированныев настоящее время процессы, которые были неспособны завершиться в ходе предыдущей операции уменьшения. Если таковые существуют, он разблокируетодин из них и продолжает работу. В противном случае он просто увеличиваетсчетчик семафора. Разблокированный процесс выполняется до вызова операцииуменьшения. Важным свойством операций с семафорами является то, что ониатомарны {atomic), то есть в случае запуска операции уменьшения или увеличения до момента ее завершения (или до момента блокировки процесса) никакойдругой процесс не может получить доступ к семафору.Известно, что программирование с использованием семафоров для синхронизации процесса вызывает множество ошибок, кроме, разве что, случаев простойзащиты разделяемых данных. Основная проблема состоит в том, что наличиесемафоров приводит к неструктурированному коду.
Похожая ситуация возникает при частом использовании печально известной инструкции goto. В качествеальтернативы семафорам многие современные системы, поддерживающие параллельное программирование, предоставляют библиотеки для реализации мониторов.Формально монитор {monitor) представляет собой конструкцию языка программирования, такую же, как объект в объектно-ориентированном программировании [200]. Монитор может рассматриваться как модуль, содержащий переменные и процедуры. Доступ к переменным можно получить только путемвызова одной из процедур монитора.
В этом смысле монитор очень похож наобъект. Объект также имеет свои защищенные данные, доступ к которым можнополучить только через методы, реализованные в этом объекте. Разница междумониторами и объектами состоит в том, что монитор разрешает выполнение процедуры только одному процессу в каждый момент времени. Другими словами,50Глава 1. Введениеесли процедура, содержащаяся в мониторе, выполняется процессом А (мы говорим, что А вошел в монитор) и процесс В также вызывает одну из процедур монитора, В будет блокирован до завершения выполнения А (то есть до тех пор, пока А не покинет монитор).В качестве примера рассмотрим простой монитор для защиты целой переменной (листинг 1.1).
Монитор содержит одну закрытую (private) переменнуюcount, доступ к которой можно получить только через три открытых (public)процедуры — чтения текущего значения, увеличения на единицу и уменьшения. Конструкция монитора гарантирует, что любой процесс, который вызываетодну из этих процедур, получит атомарный доступ к внутренним данным монитора.Листинг 1.1. Монитор, предохраняющий целое число от параллельного доступаmonitor Counterprivate:int count =public:int valueOvoid incrOvoid decrO}{0:{ return count; }{ count = count + 1;}{ count = count - 1;}Итак, мониторы пригодны для простой защиты совместно используемых данных. Однако для условной блокировки процесса необходимо большее. Например, предположим, нам нужно заблокировать процесс при вызове операцииуменьшения, если обнаруживается, что значение count равно нулю. Для этой цели в мониторах используются условные переменные {condition variables).
Это специальные переменные с двумя доступными операциями: wait (ждать) и signal(сигнализировать). Когда процесс А находится в мониторе и вызывает для условной переменной, хранящейся в мониторе, операцию wait, процесс А будетзаблокирован и откажется от своего исключительного доступа к монитору. Соответственно, процесс В, ожидавший получения исключительного доступа к монитору, сможет продолжить свою работу. В определенный момент времени Вможет разблокировать процесс А вызовом операции signal для условной переменной, которого ожидает А.
Чтобы предотвратить наличие двух активных процессов внутри монитора, мы доработаем схему так, чтобы процесс, подавшийсигнал, покидал монитор. Теперь мы можем переделать наш предыдущий пример. Монитор из листинга 1.2 — это новая реализация обсуждавшегося ранее семафора.Листинг 1.2.
Монитор, предохраняющий целое число от параллельного доступаи блокирующий процессmonitor Counter {private:int count = 0;int blockecl_procs = 0;condition unblocked:public:int valueO { return count:}1.4. Концепции программных решений51void incrO {1f (blockecl_procs == 0)count = count + 1;elsesignal( unblocked );void decrO {if (count == 0) {blocked_procs = blocked_procs + 1;wait( unblocked ):blocked_procs = blocked_procs - 1;}elsecount = count - 1:Оборотная сторона мониторов состоит в том, что они являются конструкциями языка программирования.
Так, Java поддерживает мониторы, просто разрешая каждому объекту предохранять себя от параллельного доступа путем использования в нем инструкции synchronized и операций wait и notify. Библиотечнаяподдержка мониторов обычно реализуется на базе простых семафоров, которыемогут принимать только значения О и 1. Такие семафоры часто называются переменньши-мъютексами (mutex variables), или просто мьютексами. С мьютексамиассоциируются операции lock (блокировать) и unlock (разблокировать).
Захватмьютекса возможен только в том случае, если его значение равно единице, в противном случае вызывающий процесс будет блокирован. Соответственно, освобождение мьютекса означает установку его значения в 1, если нет необходимостиразблокировать какой-нибудь из ожидающих процессов. Условные переменныеи соответствующие им операции также поставляются в виде библиотечных процедур.
Дополнительную информацию по примитивам синхронизации можнонайти в [17].Мультикомпьютерные операционные системыМультикомпьютерные операционные системы обладают гораздо более разнообразной структурой и значительно сложнее, чем мультипроцессорные. Эта разницапроистекает из того факта, что структуры данных, необходимые для управлениясистемными ресурсами, не должны больше отвечать условию легкости совместного использования, поскольку их не нужно помещать в физически общую память.