
ОС_Методические указания
.pdf
состоянии соответствующая программа обработки прерывания должна вновь запустить следующую передачу, если буфер еще не заполнен при чтении и если буфер не пуст при записи.
ПРОЧИТАТЬ(ЗАПИСЬ) |
|
Программа - |
Устройство ввода |
пользователь |
|
Буфер (очередь из N позиций)
Рис. 3.4. Схема буферного ввода данных.
Листинг 3.4. Алгоритм буферного ввода данных.
ПРОЧИТАТЬ(ЗАПИСЬ) {--вызов супервизора--}
If not БУФЕР_ПУСТОЙ then begin
ИЗЪЯТЬ(ЗАПИСЬ,БУФЕР); <Запустить передачу, если необходимо>
end
else
begin
<Запустить передачу, если необходимо>; ЖДАТЬ;
end
{--обработчик прерывания--} if not БУФЕР_ПОЛНЫЙ
then <Запустить передачу>; if ОЖИДАНИЕ
then <Возобновить чтение>
|
ЗАПИСАТЬ(ЗАПИСЬ) |
|
Устройствовывода |
Программа- |
|
пользователь |
||
|
Буфер(очередьизNпозиций)
Рис. 3.5. Схема буферного вывода данных.
Листинг 3.5. Алгоритм буферного вывода данных.
ЗАПИСАТЬ(ЗАПИСЬ) {--вызов супервизора--}
If not БУФЕР_ПОЛНЫЙ then begin
ПОМЕСТИТЬ(ЗАПИСЬ,БУФЕР);
- 21 -

<Запустить передачу, если необходимо>
end
else
begin
<Запустить передачу, если необходимо>; ЖДАТЬ;
end
{--обработчик прерывания--} if not БУФЕР_ПУСТОЙ
then <Запустить передачу>; if ОЖИДАНИЕ
then <Возобновить запись>
В приведенных выше программах «если необходимо» означает «если передача уже не осуществляется».
3.4. ЗАДАНИЯ ДЛЯ САМОСТОЯТЕЛЬНОЙ РАБОТЫ
1.Реализуйте на каком-либо алгоритмическом языке высокого уровня очередь (рис. 3.3), предназначенную для работы с записями длиной в слово. Реализация должна предусматривать следующие примитивы:
function put_word(rec:word):boolean;
Назначение: Занести запись rec в очередь.
Входные параметры:
rec - запись длиной в слово, заносимая в очередь.
Выходные параметры: нет. Выдаваемые значения:
true - операция завершилась успешно,
false - очередь переполнена, операция прервана.
function get_word(var rec:word):boolean;
Назначение: Извлечь запись из очереди.
Входные параметры: нет Выходные параметры:
rec - запись длиной в слово, извлекаемая из очереди.
Выдаваемые значения:
true - операция завершилась успешно, false - очередь пуста, операция прервана.
2.Реализуйте на каком-либо алгоритмическом языке высоко уровня очередь, предназначенную для работы с записями произвольной длины. Реализация должна предусматривать следующие примитивы:
function put_record(const rec; n:integer):boolean;
Назначение: Занести запись rec в очередь.
Входные параметры:
rec - запись длиной n байт, заносимая в очередь; n - длина записи в байтах.
Выходные параметры: нет. Выдаваемые значения:
true - операция завершилась успешно,
- 22 -

false - очередь переполнена, операция прервана.
function get_record(var rec; m:integer):integer;
Назначение: Извлечь запись из очереди.
Входные параметры:
m - максимальная длина буфера rec байтах.
Выходные параметры:
rec - буфер, куда будет копироваться извлекаемая запись.
Выдаваемые значения:
«0» - очередь пуста, операция прервана;
«-1» - длина m буфера оказалась недостаточной для размещения извлекаемой записи; положительное значение является длиной извлеченной записи в случае успешного завершения операции.
3.Качество системы может быть улучшено путем организации всех медленных процедур вводавывода через буферы на диске. Если эти буферы достаточно большие, можно опережать обмен с медленными периферийными устройствами (например, с принтерами) за счет записи на
диск или чтения с него.
ЗАПИСАТЬ_СТРОКУ
|
|
|
|
|
|
|
|
|
|
|
|
Буфер записи |
|
Буфер печати |
|||
|
|
|
|
|
|
Буфер на диске
Такой режим функционирования называется спулингом.
Разработайте алгоритм и напишите драйверы вывода данных на принтер в режиме спулинга с учетом, что принтер и канал ввода-вывода на диск могут вызывать прерывания процессора.
- 23 -
РАБОТА 4. ПРОЦЕССЫ. МЕХАНИЗМЫ СИНХРОНИЗАЦИИ ПРОЦЕССОВ
Целью работы является ознакомление с понятием процессов, способами их взаимодействия и механизмами синхронизации совокупности процессов.
4.1. ПОНЯТИЕ ПРОЦЕССА
Понятие процесса дает возможность моделировать активное состояние непрерывного выполнения программы на машине.
Пусть время, в течение которого прослеживается изменение состояния машины, представляется параметром t , принимающим последовательность возрастающих дискретных значений, которые выбираются в начале и конце выполнения каждой инструкции, т.е. в точках наблюдения (определение 1.2).
Определение 4.1
Событием назовем состояние машины, наблюдаемое в некоторый момент времени t .
События позволяют отмечать изменения состояния машины. Начало и конец каждого действия a являются событиями, соответствующие числовые значения параметра t которых обозна-
чим beg(a) и end(a) ; имеем beg(a) end(a) .
Определение 4.2
Последовательность действий a1 ,a2 , ,ai , , для каждого элемента которой beg(ai ) end(ai 1 ) , называется последовательным процессом или просто процессом.
Процесс можно описать рядом следующих друг за другом событий: beg(a1 ),end(a1 ),beg(a2 ),end(a2 ), . Такая последовательность данных и состояний машины называется временным следом (историей или трассой) процесса.
Определение 4.3
Контекстом процесса назовем ту информацию, которую действия процесса могут проверять или изменять.
Контекст процесса, связанного с выполнением программы, включает:
контекст процессора;
контекст памяти, или рабочее пространство;
совокупность атрибутов процесса, которая содержит следующие характеристики:
имя процесса, которое служит для его идентификации и обычно представляет некоторый уникальный номер;
приоритет процесса, который служит для упорядочения выделения процессора процессам;
права процесса, которые определяют перечень разрешенных операций, обеспечивающий защиту используемой информации.
4.2. ВЗАИМОДЕЙСТВИЕ СОВОКУПНОСТИ ПРОЦЕССОВ
Рассмотрим теперь выполнение совокупности процессов и их взаимодействие. Понятия, введенные в разд. 4.1, обобщаются следующим образом:
Определение 4.4
Трасса совокупности процессов - последовательность событий, составленных из данных о начале и конце каждого действия, при выполнении всех соответствующих программ.
- 24 -

Контексты разных процессов могут иметь общие части.
Определение 4.5
Два процесса называются независимыми, если их контексты не пересекаются.
Пусть имеются две разные программы P и Q со своими сегментами процедур и данных. Соответствующие им процессы обозначим p и q. Выполнение совокупности процессов (p,q) может происходить следующими путями, представленными на рис. 4.1.
|
|
|
|
p |
|
|
|
|
|
|
(1) |
|
|
|
|
|
|
|
|
q |
|
p |
|
|||||||||
|
|
|
|
|
|
|||||
|
|
p |
||||||||
(2) |
|
|
|
q |
|
|
|
|
q |
|
|
|
|
|
|
|
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
p
(3)
q
Рис. 4.1. Совместное выполнение совокупности процессов.
По схеме 1 сначала полностью выполняется один процесс, а затем - второй. Схема этого типа является схемой последовательного выполнения процессов p и q. Она описывается неравен-
ством end( p) beg(q) .
По схеме 2 поочередно выполняется последовательность инструкций p, потом последовательность инструкций q, затем вновь последовательность следующих инструкций p и т.д., пока не окончатся оба процесса. Эта схема - схема псевдопараллельного выполнения. Процессы выполняются на одном процессоре.
По схеме 3 оба процесса выполняются одновременно на различных процессорах. Схемы 2-го и 3-го типов описываются неравенствами end( p) beg(q) и end(q) beg( p) .
Очевидно, при взаимодействии совокупности процессов, если они не являются независимыми, возникает проблема разделения ими общих ресурсов. Несмотря на то, что физические и логические ресурсы можно разделять, они обычно доступны в каждый момент только одному процессу.
Определение 4.6
Ресурс, который допускает обслуживание только одного пользователя за один раз, называется критическим ресурсом.
В схеме 2 взаимодействия процессов процессор является критическим ресурсом для процессов p и q.
Если несколько процессов хотят пользоваться критическим ресурсом в режиме разделения, им следует синхронизировать свои действия таким образом, чтобы этот ресурс всегда находился в распоряжении не более чем одного из них. Если один процесс пользуется в данный момент критическим ресурсом, то все остальные процессы, которым нужен этот ресурс, временно получают отказ и должны ждать, пока он не освободится. Внутри каждого процесса можно выделить места, в которых происходит обращение к критическим ресурсам. Эти места называются критическими участками. Критические участки должны быть взаимно исключаемыми.
Приведем пример критического участка. Пусть имеются два процесса p и q, каждый из которых изменяет значение одной общей переменной n. Например, переменной n может быть со-
- 25 -

стояние банковского счета, на который p и q осуществляют вклады. Запишем соответствующие действия в программах p и q:
(процесс p) Ap: n: n 10; (процесс q) Aq: n: n 20 .
Распишем теперь эти действия в виде инструкций, обозначая соответствующие регистры
Rp и Rq.
Листинг 4.1
ПРОЦЕСС p |
;загрузить n в Rp |
ПРОЦЕСС q |
|
(1) |
mov Rp,n |
(1') mov Rq,n |
|
(2) |
add Rp,10 ;Rp:=Rp+10 |
(2') add Rq,20 |
|
(3) |
mov n,Rp |
;заслать Rp в n |
(3') mov n,Rq |
Если в результате функционирования этих двух процессов действия будут выполнены в следующем порядке: 1,1',2,2',3,3', то счет увеличится лишь на 20 единиц, а 10 единиц будут потеряны. Чтобы избежать этого, действия Ap и Aq должны выполняться в режиме взаим-
ного исключения. Эти действия образуют критические секции.
Другим характерным примером взаимодействия процессов является буферный вводвывод, в котором два процесса, выполняемых центральным процессором и каналом вводавывода, обмениваются информацией через общий буфер. Здесь процессы, находящиеся в отношении кооперации для выполнения общей цели - ввода-вывода информации, «конфликтуют» между собой за обладание разделяемым критическим ресурсом - буфером ввода-вывода. Конфликты могут выражаться в следующем:
попытка чтения из буфера, в котором нет ни одной записи;
попытка записи в переполненный буфер;
попытка чтения данных из буфера одним процессом, в то время как другой процесс выполняет запись в буфер.
Однако, так как группы операций, которые обращаются к разделяемому критическому ресурсу, являются критическими участками, то задачу обладания разделяемым критическим ресурсом можно свести к задаче взаимного исключения.
В общем случае проблему взаимного исключения можно сформулировать в следующей форме. Пусть ( p1 , , pn ) - последовательность циклических процессов, программа каждого из которых содержит критическую секцию (листинг 4.2).
Листинг 4.2
<инициализация> {--выполняется один раз до начала всех процессов}
ПРОГРАММА ПРОЦЕССА pi while true do
begin
<ПРОЛОГ>; <КРИТИЧЕСКАЯ СЕКЦИЯ>; <ЭПИЛОГ>;
<ОСТАВШАЯСЯ ЧАСТЬ ПРОГРАММЫ>;
end
4.3. СИНХРОНИЗАЦИЯ ПРОЦЕССОВ
Чтобы не допустить одновременного выполнения нескольких критических участков, в системе должен быть предусмотрен механизм, который бы синхронизировал эти процессы. Механизм этот должен обладать следующими свойствами:
- 26 -

если один или несколько процессов хотят обратиться к своим критическим участкам, то по меньшей мере один из них должен в конце концов получить разрешение войти в свой критический участок;
в каждый момент не более чем одному процессу разрешается находиться в своем критическом участке.
Блокировка памяти
Одним из способов реализации взаимного исключения является синхронизация с помощью элементарных приемов нижнего уровня - блокировки памяти. Здесь взаимное исключение реализуют аппаратно, сделав операции над памятью неделимыми. То есть если каждый из двух процессов пытается поместить какое-то значение в одну и ту же ячейку, то спор разрешается аппаратурой: одному процессу разрешается выполнить операцию засылки немедленно, а другому приходится ждать, пока первый не кончит.
Рассмотрим в качестве примера задачу синхронизации (листинг 4.2) для двух процессов. Одним из возможных решений этой задачи является алгоритм Деккера (листинг 4.3).
Листинг 4.3. Алгоритм Деккера.
{--инициализация--}
var c1,c2, ОЧЕРЕДЬ: integer; c1:=0; c2:=0; ОЧЕРЕДЬ:=1;
ПРОЦЕСС p1 |
ПРОЦЕСС p2 |
{--пролог--} |
{--пролог--} |
c1:=1; |
c2:=1; |
while c2=1 do |
while c1=1 do |
if ОЧЕРЕДЬ=2 then |
if ОЧЕРЕДЬ=1 then |
begin |
begin |
c1:=0; |
c2:=0; |
while ОЧЕРЕДЬ=2 |
while ОЧЕРЕДЬ=1 |
do; |
do; |
c1:=1; |
c2:=1; |
end; |
end; |
<КРИТИЧЕСКАЯ СЕКЦИЯ> |
<КРИТИЧЕСКАЯ СЕКЦИЯ> |
{--эпилог--} |
{--эпилог--} |
c1:=0; |
c2:=0; |
ОЧЕРЕДЬ:=2; |
ОЧЕРЕДЬ:=1; |
<ОСТАВШАЯСЯ ЧАСТЬ> |
<ОСТАВШАЯСЯ ЧАСТЬ> |
В этом алгоритме c1=1, когда p1 хочет войти в свою критическую секцию; c2=1, когда p2 хочет войти в свой критический участок, а переменная ОЧЕРЕДЬ указывает, чья очередь по-
пытаться войти при условии, что оба процесса хотят выполнить свои критические участки.
Проверка и установка
Для реализации взаимного исключения процессов во многих машинах имеются аппаратные операции, которые легче в использовании и более эффективны, чем простая и неделимая операция над памятью. Одна из таких операций - «проверка_и_установка».
К операции «проверка_и_установка» обращаются с двумя параметрами: ЛОКАЛЬНЫЙ и ОБЩИЙ. Операция берет значение параметра ОБЩИЙ и присваивает его переменной ЛОКАЛЬНЫЙ, а затем устанавливает в переменной ОБЩИЙ значение 1. Главное свойство этой операции
- 27 -

- ее неделимость. Когда процесс выполняет операцию «проверка_и_установка», между ее началом концом не может произойти никаких действий.
Переменная ОБЩИЙ разделяется между процессами, которые подлежат синхронизации по отношению к некоторому критическому ресурсу. У каждого процесса есть своя собственная переменная ЛОКАЛЬНЫЙ. Предполагается также, что в этой машине реализована блокировка памяти, т.е. операция присвоения переменной ОБЩИЙ значения неделима.
Листинг 4.4. Взаимное исключение с помощью операции «проверка_и_установка»
{--инициализация--} var
ОБЩИЙ: integer;
ПРОГРАММА ПРОЦЕССА pi
var
ЛОКАЛЬНЫЙ: integer; while true do
begin
{--пролог--}; ЛОКАЛЬНЫЙ:=1; while ЛОКАЛЬНЫЙ=1
do проверка_и_установка(ЛОКАЛЬНЫЙ,ОБЩИЙ); <КРИТИЧЕСКАЯ СЕКЦИЯ>; {--эпилог-->; ОБЩИЙ:=0;
<ОСТАВШАЯСЯ ЧАСТЬ ПРОГРАММЫ>;
end.
Монитор Хоара
Монитор состоит из множества переменных состояния и множества процедур, которые используют эти переменные. Некоторые из этих процедур, называемые внешними, доступны пользователям монитора; их имена называются точками входа в монитор. Процессы, которые используют монитор, не имеют прямого доступа к переменным состояния. Они могут воспользоваться монитором только путем вызова внешних процедур; процедуры включают операции, которые позволяют блокировать или активизировать процессы в соответствии со спецификацией задачи. Блокированные процессы группируются в очередь ожидания. Условия установки и снятия блокировки записываются в виде функций переменных состояния, и механизм работы монитора гарантирует, что все преобразования этих переменных происходят при взаимном исключении. Наконец, монитор содержит программу инициализации, которая выполняется только один раз - при создании монитора.
Базовыми примитивами монитора являются следующие процедуры:
ЖДАТЬ - блокирует процесс и ставит его в очередь ожидания;
ПУСТО - логическая функция (выдает true, если очередь ожидания пуста);
СИГНАЛИЗИРОВАТЬ - если не ПУСТО, то активизирует один процесс из очереди ожидания. Процесс, активизируемый примитивом СИГНАЛИЗИРОВАТЬ, начинает свою работу с
инструкции, идущей сразу же за примитивом ЖДАТЬ, который его блокировал.
Для описания монитора удобнее всего использовать методы объектно-ориентированного программирования, что мы в дальнейших примерах и задачах и будем делать. Базовые примитивы монитора будем считать заданными в качестве методов объекта TMonitor:
- 28 -

Листинг 4.5. Объявление монитора
type
TMonitor=object private
<внутренние переменные и очереди ожидания>; protected
procedure ЖДАТЬ;
procedure СИГНАЛИЗИРОВАТЬ; function ПУСТО: boolean;
end;
В качестве простейшего примера рассмотрим следующую задачу. Пусть имеются два процесса: p и q. Процесс p записывает данные в буфер, процесс q читает их из буфера. Процесс q не может начать чтение до завершения процессом p записи. Необходимо синхронизировать их работу.
Для решения этой задачи объявим монитор, являющийся наследником объекта TMonitor (листинг 4.6).
Листинг 4.6
type
TBufMonitor=object(TMonitor) private
ЗАВЕРШЕН: boolean; public
procedure ИНИЦИАЛИЗИРОВАТЬ; procedure КОНЕЦ_ЗАПИСИ; procedure НАЧАЛО_ЧТЕНИЯ;
end;
procedure TBufMonitor.ИНИЦИАЛИЗИРОАТЬ; begin
ЗАВЕРШЕН:=false;
end;
procedure TBufMonitor.КОНЕЦ_ЗАПИСИ; begin
ЗАВЕРШЕН:=true; СИГНАЛИЗИРОВАТЬ;
end;
procedure TBufMonitor.НАЧАЛО_ЧТЕНИЯ; begin
if not ЗАВЕРШЕН then ЖДАТЬ;
end;
Здесь переменная состояния ЗАВЕРШЕН сигнализирует о том, занят ли буфер каким-либо процессом. Процедуры КОНЕЦ_ЗАПИСИ и НАЧАЛО_ЧТЕНИЯ должны быть неделимыми.
Монитор используется следующим образом.
- 29 -

Листинг 4.7
{--общая часть--} var m:TBufMonitor;
m.ИНИЦИАЛИЗИРОВАТЬ;
ПРОЦЕСС p |
ПРОЦЕСС q |
ЗАПИСАТЬ(a); |
<начало q> |
m.КОНЕЦ_ЗАПИСИ; |
m.НАЧАЛО_ЧТЕНИЯ; |
<продолжение p> |
ПРОЧИТАТЬ(a) |
Семафоры
Семафор состоит из счетчика СЧ с целыми значениями и очереди ожидания. При инициализации семафора счетчик принимает начальное значение C0 0 , а очередь пуста. Семафор может управляться только двумя примитивами P и V, которые являются неделимыми; значения счетчика и очередь ожидания недоступны даже для чтения.
Когда некий процесс выполняет примитив P:
СЧ:=СЧ-1;
если СЧ 0, то процесс продолжает работу;
если СЧ<0, то процесс блокируется и становится в очередь ожидания; он остается заблокированным до тех пор, пока примитив P, выполненный другим процессом, не освободит его.
Когда какой-либо процесс выполняет примитив V:
СЧ:=СЧ+1;
если СЧ>0, то процесс продолжает работу;
если СЧ 0, то один процесс удаляется из очереди ожидания и получает разрешение продолжить работу; процесс, который обратился к операции V, тоже может продолжать работу.
4.4. ЗАДАНИЯ ДЛЯ САМОСТОЯТЕЛЬНОЙ РАБОТЫ
1. Рассмотрим набор из восьми частично упорядоченных процессов {A,B,C,D,E,F,G,H}, где
end( A) beg(B) , |
end( A) beg(C) , end( A) beg(E) , end(E) beg(F) , end(D) beg(F) , и |
||
end(C) beg(D) , |
end(B) beg(D) , |
end(F) beg(G) , |
end(F) beg(H) . Изобразите |
выполнение этой последовательности процессов в виде программы, используя, где возможно, параллелизм процессов. Фрагменты программ, которые могут выполняться параллельно, заключите между ключевыми словами parbegin и parend.
2.Выполните задание 1 при дополнительном ограничении end(E) beg(C) .
3.Пусть имеется следующий алгоритм синхронизации двух процессов с использованием блокировки памяти.
Листинг 4.8
{--общая часть--}
var sw1,sw2: boolean; sw1:=false; sw2:=false;
ПРОЦЕСС 1 |
ПРОЦЕСС 2 |
while true do |
while true do |
begin |
begin |
while sw2 do; |
while sw1 do; |
sw1:=true; |
sw2:=true; |
<критический участок> |
<критический участок> |
sw1:=false; |
sw2:=false; |
- 30 -