Санкт-Петербургский Государственный Электротехнический Университет Кафедра: АСОИУ Лабораторная работа №5 по курсу «Операционные среды АСОИУ» ИССЛЕДОВАНИЕ МЕТОДОВ СИНХРОНИЗАЦИИ ПРОЦЕССОВ Преподаватель: Широков Владимир Владимирович Выполнили: Студенты гр.9331 Захаров Илья Мазманов Денис Степуленок Денис Студентка гр.9332 Воронкина Ирина Санкт-Петербург 2003 Цель работы - знакомство с методами синхронизации процессов на основе семафоров. Общие сведения Синхронизацией называется обеспечение заданной очередности прохождения процессов через определенные состояния. Наиболее часто синхронизация требуется для координации доступа нескольких процессов к одному разделяемому ресурсу. Рассмотрим простейший пример. Предположим, что два процесса выводят информацию в виде символа в разные точки экрана. Фрагмент текста процедур, соответствующих процессам, выглядит следующим образом: Процесс 1 Процесс 2 (1) GoTo(X,Y); (3) GoTo(X,Y); (2) Write(Ch); (4) Write(Ch); При выполнении процессов в режиме разделения времени возможна ситуация, когда оператором GoTo(X,Y) курсор устанавливается в нужную точку экрана одним процессом, затем диспетчер передает управление другому процессу, который в это время выполняет операцию вывода символа. То есть очередность выполнения действий такова: 1, 3, 2, 4 или 3, 1, 4, 2. Очевидно, что в этом случае один из процессов выведет информацию не в то место экрана, куда планировал. Суть исправления ошибки состоит в обеспечении неделимости выполнения последовательности действий GoTo(X,Y) и Write(Ch). В более общем смысле ситуация представляется следующим образом. Экран в данном случае выступает в роли разделяемого ресурса, который совместно и одновременно используют два процесса. Ошибка возникает при отсутствии регламентации доступа к разделяемому ресурсу, которая заключается в том, что если один процесс работает с общим ресурсом, то другой не должен в это же время работать с ним. Используемый совместно несколькими процессами ресурс называется критическим, участок программы процесса, реализующий работу с критическим ресурсом, называется критическим участком, а режим, при котором только один процесс в произвольный момент времени может работать с критическим ресурсом (или находиться в критическом участке), называется режимом взаимного исключения. В системах реального времени пренебрежение регламентацией доступа нескольких процессов к критическому ресурсу может приводить к катастрофическим последствиям. Методы обеспечения режима взаимного исключения Можно выделить три принципиальных пути обеспечения режима взаимного исключения. 1) Запрет прерываний на входе в критический участок и разрешение прерываний на выходе из него. В этом случае фрагмент программы работы с критическим участком выглядит следующим образом: Запрет прерываний: Критический участок; Разрешение прерываний. Первым и единственным в свой критический участок войдет тот процесс, который первым доберется до инструкции CLI в функции "Запрет прерываний". Прерывания будут запрещены, диспетчер прекратит работу и все другие процессы естественным образом будут приостановлены. После выхода из критического участка прерывания будут разрешены и какой-то другой процесс сможет войти в свой критический участок. Такой способ организации взаимного исключения обладает существенными недостатками: - при запрете прерываний система становится слепой и глухой к внешним воздействиям окружающей среды, которые формируются, как правило, через систему прерываний; - приостанавливаются даже те процессы, которые вообще не работают с данным ресурсом. 2 Поэтому данный способ организации взаимного исключения применяется в тех случаях, когда критический участок очень короткий, например, несколько инструкций. 2) Активное ожидание. В этом случае вводится некоторый флаг занятости ресурса, который проверяется процессом перед тем, как войти в критический участок. Если флаг, который вначале устанавливается в состояние СВОБОДЕН, находится в состоянии СВОБОДЕН, то процесс переводит его в состояние ЗАНЯТ и входит в критический участок. При выходе из него процесс устанавливает флаг в состояние СВОБОДЕН. Если при подходе к критическому участку флаг оказывается в состоянии ЗАНЯТ, то процесс начинает проверять состояние флага в цикле до тех пор, пока флаг не будет установлен в состояние СВОБОДЕН другим процессом. Фрагменты процедуры, описывающие процесс на этапах входа в критический участок и выхода из него, выглядят следующим образом: Инициализация: FLAG := СВОБОДЕН; ... While FLAG = ЗАНЯТ Do Begin End {While}; FLAG := ЗАНЯТ; Критический участок; FLAG := СВОБОДЕН; В данном случае сам флаг является критическим ресурсом и доступ к нему должен производиться в режиме взаимного исключения. Фрагмент входа в критический участок с проверкой и установкой флага в режиме взаимного исключения выглядит следующим образом (состоянию ЗАНЯТ соответствует FLAG = 1): LBL : STI CLI CMP JZ MOV STI FLAG, 1 LBL FLAG, 1 Если чтение и установку флага выполнять за одну инструкцию, которая является неделимым действием, то вход в критический участок можно выполнить более элегантно, а именно: MOV LBL : XCHG CMP JZ AX, 1 AX, FLAG AX, 1 LBL Недостатком приведенного метода реализации взаимного исключения является активное ожидание, т. е. процесс, который ждет освобождения ресурса, занимает процессор в отводимые ему диспетчером кванты времени, хотя и не выполняет никаких полезных действий. Поэтому в случае ожидания освобождения ресурса целесообразно извлечь процесс из очереди готовых процессов и не предоставлять ему бесполезно используемых квантов времени. На этом положении основано использование семафоров как средств взаимного исключения при доступе к критическому ресурсу. 3) Семафоры. Семафор представляет собой объект, включающий счетчик и очередь. В очередь помещаются процессы, ждущие наступления некоторого события, например освобождения ресурса. Условия помещения процесса в очередь и извлечения из нее с целью активизации определяются состоянием счетчика и проверяются двумя операциями над семафором, которые называются P- и V- операциями. Принципы работы с семафором можно описать следующим образом. В исходном состоянии семафор открыт. Процесс проходит через открытый семафор в критический участок и закрывает за собой семафор. Другой процесс, подходя к критическому участку, натыкается на закрытый семафор и вынужден ждать его открытия в очереди семафора. Процесс, выходя из критического участка, открывает семафор и активизирует первый в очереди семафора процесс, который теперь может войти в критический участок и также закрыть за собой семафор. Состояние счетчика семафора играет роль индикатора занятости ресурса. Принято инициализировать счетчик в 1, декрементировать его при подходе к критическому участку (Р операция) и инкрементировать при выходе из критического участка (V - операция). Таким образом, равенство нулю счетчика свидетельствует о возможности входа в критический участок, а отрицательное значение счетчика свидетельствует о наличии процесса в критическом участке и 3 необходимости блокировки, т. е. о переводе в очередь семафора с передачей управления другим процессам. Технология реализации семафора Технология реализации семафора представлена в виде описания семафора как объекта языка программирования. Type PSemaphore = ^TSemaphore; TSemaphore = Object Счетчик : Целое; Очередь_семафора : Очередь процессов; Constructor Init(С : Целое); Destructor Done; Virtual; Procedure P; Procedure V; End {TSemaphore}. Constructor TSemaphore.Init(Целое); Begin Счетчик := С; Создать Очередь_семафора; End {TSemaphore.Init}; Destructor TSemaphore.Done; Begin Разрушить Очередь_семафора; End {TSemaphore.Done}; Procedure TSemaphore.P; Var Предыдущий : Процесс; Begin Запретить_прерывания; Счетчик := Счетчик - 1; If Счетчик < 0 Then Begin {блокировать процесс} Предыдущий := Текущий; Очередь_семафора.Включить(Предыдущий); Текущий := Очерередь_готовых.Первый; Очередь_готовых.Извлечь(Текущий); Передать_управление(Предыдущий, Текущий); End {If}; Разрешить_прерывания; End {TSemaphore.P}; Procedure TSemaphore.V; Var Предыдущий : Процесс; Begin Запретить_прерывания; Счетчик := Счетчик + 1; If Счетчик <= 0 Then Begin {активизировать процесс} Предыдущий := Текущий; Очередь_готовых.Включить(Предыдущий); Текущий := Очередь_семафора.Первый; Очередь_семафора.Извлечь(Текущий); Передать_управление(Предыдущий, Текущий); End {If}; Разрешить_прерывания; End {TSemaphore.V}. Здесь процесс, вызвавший метод TSemaphore.V, переводится в очередь готовых и активизирует процесс, стоящий первым в очереди семафора. 4 Технология использования семафоров В первую очередь семафоры используются как средства взаимного исключения при доступе к критическому ресурсу. Правило использования семафора в этом случае представлено в виде фрагмента процедуры, описывающей процесс при входе в критический участок и выходе из него. Раздел описания: Var Semaphore : TSemaphore; Раздел инициализации: Semaphore.Init(1); Процесс при работе с критическим участком: Semaphore.P; Критический участок; Semaphore.V; Завершение работы с семафором: Semaphore.Done; Семафоры могут быть использованы не только для координации доступа к критическому ресурсу нескольких процессов, но и для установления требуемой очередности прохождения процессами определенных состояний. Пусть, например, необходимо, чтобы процесс Р1 прошел через состояние, отмеченное меткой М_Р1, например чтение из ячейки памяти П, только после того, как процесс Р2 пройдет через состояние, отмеченное меткой М_Р2, например запись в ячейку памяти П, т.е. запись должна произойти раньше чтения. С помощью семафора данная задача решается следующим образом: Инициализация семафора: Процесс Р1 Semaphore.P; М_Р1 : Чтение из П; Semaphore.Init(0); Процесс Р2 М_Р2 : Запись в П; Semaphore.V; Если процесс Р1 подойдет к метке М_Р1 раньше, чем процесс Р2 - к метке М_Р2, то он будет вынужден блокироваться в очереди семафора. Процесс Р2, выполнив запись в ячейку П, вызовет Semaphore.V и тем самым активизирует процесс Р1, позволив ему выполнить чтение только после того как осуществлена запись. Часто процессы реализуются в виде бесконечных циклов, поэтому, чтобы избежать нескольких записей в ячейку П одним процессом до момента чтения из нее другим процессом, необходимо последующие записи осуществлять только после очередного чтения. Указанная схема реализуется следующим образом: Инициализация семафора: Semaphore1.Init(0); Инициализация семафора: Semaphore2.Init(0); Процесс Р1 While True Do Begin . . . Semaphore1.P; М_Р1 : Чтение из П; Semaphore2.V; . . . End {While}; Процесс Р2 While True Do Begin . . . М_Р2 : Запись в П; Semaphore1.V; Semaphore2.P; . . . End {While}. Теперь процесс Р2, выполнив запись и сигнализировав об этом вызовом Semaphore1.V, ожидает, приостановленный вызовом Semaphore2.P, чтения процессом Р1, о чем Р1 будет сигнализировать вызовом Semaphore2.V. В этой схеме методы семафора Semaphore2 служат для ожидания и посылки квитанции, подтверждающей чтение. 5 Содержание задания 1. Реализовать объект - семафор, используя средства, разработанные при выполнении лабораторных работ 1 - 4. 2. Написать демонстрационную программу, иллюстрирующую координацию доступа к критическому ресурсу с помощью реализованного семафора. 3. Написать демонстрационную программу, иллюстрирующую синхронизацию прохождения процессов через определенные состояния с помощью реализованного объекта - семафор. Отчет должен содержать текст библиотечного модуля, включающего описание и реализацию объекта - семафор, тексты демонстрационных программ с комментариями, таблицы состояний счетчиков семафоров при различных вариантах очередности прохождения процессов (для числа процессов, большего чем 2) через вызовы P и V используемых семафоров. 6