Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Операционные системы реального времени и технологии разработки кроссплатформенного программного обеспечения. Ч.1. Учебное пособие
.pdf
5.3. Современные механизмы синхронизации потоков
91
Ресурс, который предоставляется монопольно только одному из
многих процессов, называется “критическим ресурсом”. Современные ОС
РВ предоставляют несколько стандартных объектов синхронизации:
критический раздел;
мьютекс;
семафор;
событие.
Все они, кроме критического раздела, являются объектами ядра опе-
рационной системы и обрабатываются инструментами ядра.
Как и во многих других случаях, для обеспечения взаимодействия
между задачами и их синхронизацией, строят ряд очередей:
очередь выполняемых задач;
очередь готовых задач;
очередь задач в ожидании семафора;
очередь задач в ожидании события.
Очередь выполняемых задач состоит из задач, которые находятся в
состоянии исполнения, т.е. получающих рабочее время и ресурсы процессора в соответствии с тем или иным алгоритмом.
Очередь готовых задач принимает в себя задачи, которые находятся
в состоянии готовности.
Очередь задач в ожидании семафора строится для каждого создан-
ного семафора в системе.
Очередь задач в ожидании события, аналогично предыдущей, стро-
ится для каждого созданного в системе события.
Все эти очереди относятся к базовым очередям. На их основе строятся другие очереди. Например, очередь для отправки писем и сообщений
строятся на базе очередей семафоров и событий: одна очередь создается
для хранения сообщений для отправки, другая очередь – для сообщений,
которые ждут освобождения места в почтовом ящике.
Очереди любого типа создаются и обслуживаются либо операционной системой, либо аппаратно. Поэтому все операции с очередями приводят к системным вызовам. Каждый вызов проходит в ядро системы и в менеджер задач и памяти, поэтому использование очередей сопряжено с дополнительными расходами.

5. Синхронизация и взаимодействие процессов
92
Еще одним рассмотренным ранее механизмом синхронизации является критический раздел или критическая секция. Это некоторый объект, к
которому происходит обращение потока перед получением монопольного
доступа к какому-нибудь ресурсу. Критический раздел достаточно просто
организовать, например, по сравнению с очередью, потому что синхронизуемые объекты принадлежат к одному процессу. Эта особенность приводит к тому, что в одно и то же время только один поток получает доступ к
определенному классу (региону, области) ресурсов. Критический раздел и
обслуживающие его программные средства анализируют значение специального параметра процесса (флага), который предотвращает, например,
исполнение одного участка кода разными потоками одновременно или изменение одного элемента данных в разных задачах.
Следующим рассмотренным методом синхронизации является использование семафоров.
Модель семафоров была предложена голландским ученым
Дейкстройном и основывается на том, что в систему вводится специальная
модель данных, называемых семафорами. Переменная типа семафор имеет
целочисленный тип и на ней разрешено только два типа операций:
опустить (Down);
поднять (Up).
Объекты типа семафор применяются для контроля и учета ресурсов
в целях ограничения доступа к этим ресурсам со стороны сразу нескольких
потоков. Введя семафоры в систему, можно организовать работу программ
таким образом, что доступ к ресурсам будет разрешен сразу от имени нескольких потоков, но их число будет ограничено. Значение переменной семафора как раз и говорит о том, какое максимальное количество потоков
могут работать с ресурсом.
Если в какой-то момент времени с ресурсом работает меньшее число
потоков, чем указано в семафоре, то семафор будет находиться в так называемом “сигнальном” состоянии. Как только число потоков превышает значение, указанное в переменной семафора, он перекачается в состояние запрета и доступ большего количества ресурсов блокируется.
Операция “опустить” семафор применяется для контроля ресурсов
и ограничения одновременного доступа к ресурсу со стороны нескольких

5.3. Современные механизмы синхронизации потоков
93
потоков. Применяя семафор, можно устроить работу программ таким образом, что к одному определенному ресурсу смогут обращаться только
ограниченное число потоков. Как только число работающих с ресурсом
потоков достигнет предела, доступ будет заблокирован. Применение операции “опустить” семафор производится как инкремент значения (увеличение на единицу).
Операция “поднять” семафор вызывает его проверку и, если значе-
ние семафора больше нуля, то производится его декремент (т.е. уменьшение счетчика на единицу).
Здесь важно понимать, что операции с семафором похожи на тран-
закции в базе данных. Это значит, что они проводятся от начала и до конца
без прерывания процесса. Они могут называться атомарными.
Семафор, который может принимать только два значения: ноль или
единица, называется бинарным или двоичным. Захват семафора вызывает
его переключение в другое состояние, а все остальные задачи будут ждать
его возврата в исходное значение.
Другое применение счетчиков – семафоров, которые могут принимать больше двух значений, можно описать следующим образом. Допустим, в системе есть множество устройств вывода информации, например,
принтеры количеством 3 (три) штуки. Семафор устройств инициализируется со значением 3 (три) и каждый раз, когда некоторая задача делает запрос к семафору для печати документа, значение семафора декрементируется. После завершения печати семафор освобождается и его значение инкрементируется. Если текущее значение семафора нулевое, то ресурс считается недоступным, пока его значение не станет отличным от нуля. Это
состояние эквивалентно состоянию освобождения очереди печати для трех
устройств.
Семафоры являются достаточно эффективным решением для синхронизации процессов и задач, однако программирование с учетом семафоров – это сложный процесс, сопряженный с решением многих задач. Лю-
бая ошибка может привести к рассинхронизации процессов, к образованию
тупиковых ситуаций и даже к сбоям.
С целью облегчить труд разработчиков, применяются другие способы синхронизации и дополнительные инструменты.

5. Синхронизация и взаимодействие процессов
94
5.4. Мониторы
Идея мониторов была предложена Хоаром в 1974 г. В отличие от
других способов синхронизации, монитор в данном контексте представляет собой некоторую конструкцию на языке программирования, т.е. программный инструмент, поддерживаемый на уровне компиляции программ,
а значит предоставляемый на уровне языка программирования.
Монитор являет собой совокупность процедур и библиотечных
функций, а также структур данных, компилируемых в специальный программный модуль.
Для мониторов выделяют три основных свойства или условия.
1. Входящие в монитор структуры данных доступны только для тех
процедур, которые входят в данный монитор, т.е. монитор представляет собой некоторый вариант объекта (как в объектно-ориентированных языках
программирования) и реализует сокрытие (т.е. инкапсуляцию) данных.
2. Любой процесс может войти в монитор после вызова одной из
его процедур.
3. В любой момент в монитор входит не более одного процесса,
если есть несколько процессов, которым необходимо попасть в уже занятый монитор, то монитор блокируется. Таким образом, если необходимо,
например, обеспечить защиту разделяемых данных, то можно просто поместить их внутрь монитора вместе с критическими секциями обслуживающих их процедур.
Компилятору языка программирования, в котором существуют про-
граммные конструкции мониторов, известно, что входящие в них процедуры имеют определенную, выделяющуюся из общей массы семантику.
Поэтому уже после проверки первого же условия (см. список выше) программы непосредственно на этапе компиляции, становится известно, является ли данный участок кода монитором или нет. Также можно спокойно
обеспечить выполнение третьего свойства или условия существования монитора (прием только одного процесса).
Монитор является достаточно надежным инструментом синхрони-
зации, так как:
он имеет определенную структуру;
формируется компилятором;

5.4. Мониторы
95
обслуживается языком программирования;
большинство ошибок могут быть обнаружены компилятором и
не попадут в финальную сборку программы.
Помимо обычных структур данных, мониторы могут включать в
себя определенные специальные переменные, содержащие в себе операторы условий (например, wait или signal).
Эти переменные являются дополнительным инструментом синхронизации помимо основных алгоритмов.
Если процесс, входящий в монитор и исполняющий одну из процедур монитора, обнаруживает, что он не может продолжать свою работу в
силу ряда определенных причин (например, до сих пор не готово внешнее
устройство, буфер данных переполнился, другая функция не обновила данные в разделяемом ресурсе и т.д.), то он вызывает выполнение особой операции wait с отдельной переменной или условием. Дальнейшее выполнение
текущего процесса в мониторе блокируется, пока операция wait не завершится, т.е. не закончится ожидание окончания блокирующего события.
С другой стороны, если некоторый процесс производит действия,
которые могут снять блокировку выполнения основной операции монитора, вызванной командой wait, то он должен вызвать команду signal для
разблокировки.
То есть, другими словами, если один процесс Х ожидает чего-то от
процесса Y в мониторе с командой wait, то процесс Y, закончив операцию,
должен фактически уведомить процесс Х об этом событии.
Тонкость данных манипуляций заключается в том, что, как следует
из самого определения монитора, только один процесс может занимать монитор. А в описанном выше примере участвует два процесса.
Создатель технологии мониторов описал эту ситуацию следующим
образом: если процесс, вызвавший signal, приостанавливается, то в монитор может войти процесс, ожидающий сигнал завершения с командой wait.
В 1975 г. было получено более простое описание данной ситуации:
вызов команды signal должен быть последним внутри процедуры монитора
(должен быть последней командой), чтобы процесс покинул монитор сразу
после вызова сигнала.
Основное достоинство мониторов заключается в том, что взаимное
исключение здесь возникает автоматически, так как в одно время только

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

5.5. Обмен сообщениями
97
Рассмотрим обобщенный пример применения системы обмена сооб-
щениями и его основную концепцию.
Допустим, у нас есть несколько программных агентов, между кото-
рыми необходимо провести синхронизацию и обмен данными.
Следовательно, в самом простом случае, программные агенты
должны иметь доступ к шине для обмена сообщениями и как минимум две
реализации стандартных процедур:
отправка сообщения;
прием сообщения.
Дополнительно к этим двум функциям, конечно, реализуют функ-
ции ожидания сообщения (методом опроса или методом сигналов) и функции разбора сообщений с анализом адреса сообщения.
Как и семафоры, эти процедуры реализуются системными вызовами,
а не конструкциями конкретного языка программирования.
Система сообщений обладает следующими стандартными характе-
ристиками.
1. Способ синхронизации.
2. Метод адресации.
3. Длина сообщения (минимальная и максимальная).
Рассмотрим их последовательно.
Способ синхронизации в системе сообщения подразумевает сам
смысл и содержание обмена данными, оформленными в виде отдельных пакетов – сообщений. Пакет выпускает источник, а принимает один или несколько приемников сообщений. Если приемник один, и он определен заранее – сообщение считается адресным. Если приемников несколько, и их состав определен заранее – сообщение считают групповым. Если принимают
сообщения все приемники – сообщение считают широковещательным.
Весь процесс трансляции сообщений прост и функционален: один
источник формирует сообщение, помещает его в очередь, которую “слушают” приемники. Однако иногда возникает ситуация, когда некоторый
приемник ожидает сообщение, а передатчик его не посылает, или наоборот
– передатчик отправляет сообщение, которое некому принимать или приемник еще не готов к приему. В этом случае, как операция передачи, так и
операция приема могут быть блокирующими или неблокирующими. Для
операции отправки (send) это означает, что процесс – отправитель может

5. Синхронизация и взаимодействие процессов
98
быть заблокирован до того момента, пока процесс – приемник напрямую
не вызовет операцию receive. В исключительных случаях, отправка сообщения производится независимо от наличия получателя в системе (тогда сообщение “оседает” в очереди до появления получателя). Для операции приема
receive это означает, что операция получения возникает ранее, чем фактически было послано сообщение – в этом случае она блокируется до самого события получения сообщения с последующим возвратом управления.
В связи с этим существует несколько схем и комбинаций операций
отправки и получения:
схема рандеву;
неблокирующая отправка и блокирующий прием;
неблокирующая отправка и неблокирующий прием.
Схема рандеву подразумевает, что процедура отправки является
блокирующей так же, как и операция приема. Они не требуют буферизации
сообщений и часто применяются именно как элемент синхронизации.
Неблокирующая отправка и блокирующий прием возникают тогда,
когда реализуется схема работы типа клиент/сервер. Здесь серверный процесс блокируется до тех пор, пока не возникнет очередной запрос для обработки. В это время клиент, пославший запрос к серверу, может продолжить свое выполнение, не ожидая фактического окончания обработки своего запроса на сервере.
Неблокирующая отправка и неблокирующий прием наблюдается тогда, когда оба процесса могут продолжать свое выполнение, не дожидаясь
окончания коммуникации между собой.
Здесь очень важно понимать, что в том случае, когда отправка является неблокирующей, процесс, отправивший сообщение, не имеет информации о том, получено и обработано ли без ошибок его сообщение. Это
происходит потому, что ответа от получателя отправитель не имеет.
В данном случае можно говорить о “гарантированной” доставке сообщений, когда есть какая-либо обратная связь и отправитель получает информацию о получении сообщения адресатом. Такой механизм основывается на дополнительной подсистеме сообщений-подтверждений. То есть
каждое большое информационное сообщений сопровождается обратной

5.5. Обмен сообщениями
99
“квитанцией” о доставке. Однако, если квитанция была потеряна в ходе обратной доставки, процесс может быть навсегда заблокирован, так как он
будет ждать подтверждения, которое он не получит никогда.
Проблема возникает, когда операция приема receive является блоки-
рующей. Выходом из такой ситуации является внедрение еще одного механизма, позволяющего проверить получение, но без блокировки отправителя.
Метод адресации. Это другая важная проблема в синхронизации. Она
заключается в организации правильной адресации сообщений. Адресация
может быть косвенной или прямой. При косвенной адресации, идентификация получателя и отправителя производится в некотором адресном или именованном подпространстве (поддомене). Метод ненадежный и зависящий от
множества факторов. Чаще всего применяется адресация абсолютная на базе
некоторых идентификаторов, которые зачастую делаются уникальными не
только в рамках данной системы, но и уникальными вообще. Для генерации
подобных идентификаторов существуют отдельные алгоритмы, создающие
числовые или символьные последовательности большой длины, зависящие
от общего мирового времени и случайных чисел.
Отправитель может указать явно идентификатор получателя, которому ему необходимо доставить сообщение или указать группу идентификаторов, например, сгенерированных в определенной последовательности.
В любом случае, почтовый ящик формируется как очередь сообщений, которая по своей сути является буфером, рассчитанным на определенное число сообщений. Буфер имеет ограничение либо по объему занимаемой памяти, либо по числу хранимых сообщений. В таком случае, сообщения уже могут отправляться не отдельным процессам, а целым почтовым
ящикам, из которых они уже могут распределяться по получателям. Это
напоминает корпоративную почту предприятия, сообщения которого отправляются на один и тот же адрес почты, а до адресата доходят в зависимости от темы или содержания.
Связь между процессом-получателем и процессом отправителем через почтовый ящик может быть косвенной или прямой, статической или
динамической. Статическое связывание выполняется один раз и навсегда
при создании ящика для очереди сообщений. Динамическое связывание использует трансляцию адресом отправителя и получателя и дополнительные
операции соединения/разъединения (connect/disconnect).

5. Синхронизация и взаимодействие процессов
100
Кроме того, ящик обычно создается как самостоятельный объект со
своими свойствами и методами. Например, к ящику могут быть применены
операции очистки/удаления/создания (clear/create/destroy).
Над почтовыми ящиками могут выполняться следующие простые
операции.
1. Помещение сообщения в почтовый ящик. Положить сообщение
в почтовый ящик может некоторая задача, у которой есть на это право. При
этом данная задача может быть блокирована и перемещена в специальную
очередь ожидающих отправку задач, если в ящике нет свободного места.
2. Попытка помещения сообщения в почтовый ящик. Задача может
попробовать положить сообщение в почтовый ящик. При этом она получает либо положительный ответ, либо признак того, что место в ящике отсутствует.
3. Помещение сообщения в начало очереди почтового ящика. Опе-
рация выполняется так же, как и первая, только сообщение помещается в
“голову” (т.е. в начало) очереди.
4. Попытка помещения сообщения в начало очереди почтового
ящика. Эквивалентна предыдущей операции “попытка помещения сообщения”, только система попробует положить сообщение в начало очереди.
5. Выборка сообщения из почтового ящика. Данная операция вы-
полняется задачей, имеющей право читать сообщение из почтового ящика.
При этом задача может быть блокирована и перемещена в очередь задач
для ожидания возможности получения сообщения. Это происходит в основном тогда, когда в ящике нет сообщений вообще или в очереди ожидающих уже есть другие задачи. Здесь используется механизм взаимного исключения, т.е. пока выполняется операция выборки для одной задачи, все
другие ожидающие задачи будут заблокированы.
6. Попытка выборки сообщения из почтового ящика. Действие по-
хоже на предыдущее за исключением того, что задача не блокируется, а просто получает признак того, можно ли вернуть сейчас сообщение или нет.
7. Очистка ящика. Операция удаления всех сообщений из очереди
и очистка очереди ожидающих задач (опционально).
Длина сообщения – это третий из рассмотренных выше аспектов
синхронизации сообщениями.
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
