Учебная деятельность    

Операционные системы

Самостоятельная работа №3B

Исследование методов буферизации сообщений

Цель работы: изучение метода обмена сообщениями между процессами с помощью буфера.

Общие сведения

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

Существуют следующие требования к алгоритмам функционирования буфера:

Как правило, механизмы синхронизации записи в буфер и чтения из буфера являются скрытыми для пользователя, которым предоставляются лишь примитивы СОЗДАТЬ, УНИЧТОЖИТЬ, ЗАПИСАТЬ и ПРОЧИТАТЬ, внешне напоминающие работу с файлами.

Простейший вариант синхронизации записи и чтения для буфера размером в 1 ячейку памяти рассмотрен в практическом задании 3A. В данной работе рассматривается общий случай буфера размером в N элементов.

Структура буфера.

Буфер представляет собой массив из N элементов определенного типа. Состояние буфера описывается количеством n сообщений, находящихся в буфере, и двумя индексами – индексом out чтения и индексом in записи.

Запись в буфер предваряется проверкой условия "буфер полон", то есть n = N, чтение из буфера предваряется проверкой условия "буфер пуст", то есть n = 0.

Выполнение условия "буфер полон" означает, что скорость записи опередила скорость чтения, а выполнение условия "буфер пуст" означает, что скорость чтения опередила скорость записи. В нормальном состоянии значение индекса записи немного превышает значение индекса чтения.

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

Синхронизация записи и чтения реализуется использованием очередей ожидания двух видов:

Описание буфера

Представим буфер в виде объекта:

        TBuffer = Object
           in, out    : [0..N-1];
           n          : [0..N];
           Buf        : Array[0..N-1] Of AnyType;
           ReadList, WriteList  : TList;
           Constructor Init;
           Destructor  Done; Virtual;
           Procedure   Write(M : AnyType);
           Procedure   Read(Var M : AnyType);
           Procedure   Wait_Read;
           Procedure   Signal_Read;
           Procedure   Wait_Write;
           Procedure   Signal_Write;
        End {TBuffer}.
Constructor TBuffer.Init;
Begin
        in  := 0; out := 0; n := 0;
        ReadList.Init;
        WriteList.Init;
End {TBuffer.Init};
Destructor TBuffer.Done;
Begin
        ReadList.Done;
        WriteList.Done;
End {TBuffer.Done};

Синхронизация записи и чтения реализуется следующими четырьмя методами объекта-буфер.

Procedure TBuffer.Wait_Read;
{заставляет процесс ждать чтения, если буфер пустой}
Var
          Предыдущий : Процесс;
Begin
          Предыдущий := Текущий;
          ReadList.Включить(Предыдущий);
          Текущий := Очерередь_готовых.Первый;
          Очередь_готовых.Извлечь(Текущий);
          Передать_управление(Предыдущий, Текущий);
End {TBuffer.Wait_Read};
Procedure TBuffer.Wait_Write;
{заставляет процесс ждать записи, если буфер полный}
Var
          Предыдущий : Процесс;
Begin
          Предыдущий := Текущий;
          WriteList.Включить(Предыдущий);
          Текущий := Очерередь_готовых.Первый;
          Очередь_готовых.Извлечь(Текущий);
          Передать_управление(Предыдущий, Текущий);
End {TBuffer.Wait_Write};
Procedure TBuffer.Signal_Read;
{"сигнализирует" о том, что произведена запись и возможна активи-
  зация одного из процессов, ждущих чтения}
Var
          Локальный : Процесс;
Begin
          Локальный := ReadList.Первый;
          If Локальный <> NIL Then Begin   {очередь не пустая}
             ReadList.Извлечь(Локальный);
             Очередь_готовых.Включить(Локальный);
          End {If};
End {TBuffer.Signal_Read}.
Procedure TBuffer.Signal_Write;
{"сигнализирует" о том, что произведено чтение и возможна активи-
  зация одного из процессов, ждущих записи}
Var
          Локальный : Процесс;
Begin
          Локальный := WriteList.Первый;
          If Локальный <> NIL Then Begin   {очередь не пустая}
             WriteList.Извлечь(Локальный);
             Очередь_готовых.Включить(Локальный);
          End {If};
End {TBuffer.Signal_Write}.
Procedure TBuffer.Write(M : AnyType);
Begin
        Запретить прерывания;
        If n = N Then Wait_Write; {буфер полный}
        n       := n + 1;
        Buf[in] := M;
        in      := (in + 1) MOD N;
        Signal_Read;
        Разрешить прерывания;
End {TBuffer.Write};
Procedure TBuffer.Read(Var M : AnyType);
Begin
        Запретить прерывания;
        If n = 0 Then Wait_Read; {буфер пустой}
        n   := n - 1;
        М   := Buf[out];

                             - 31 -
        out := (out + 1) MOD N;
        Signal_Write;
        Разрешить прерывания;
End {TBuffer.Read};

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

Procedure TBuffer.Signal_Read;
Var
          Предыдущий, Локальный  : Процесс;
Begin
          Локальный := ReadList.Первый;
          If Локальный <> NIL Then Begin   {очередь не пустая}
             Предыдущий := Текущий;
             Текущий    := Локальный;
             ReadList.Извлечь(Локальный);
             Очередь_готовых.Включить(Предыдущий);
             Передать_управление(Предыдущий, Текущий);
          End {If};
End {TBuffer.Signal_Read}.

Задание

  1. Реализовать объект-буфер в библиотечном модуле для некоторого типа передаваемых данных.
  2. Написать демонстрационную программу, иллюстрирующую работу буфера при различных скоростях записи и чтения сообщений. Скорости записи и чтения можно менять путем изменения количества процессов, пишущих в буфер или читающих из буфера, или включая операторы задержки между следующими действиями:
    • порождением сообщения и записью его в буфер;
    • чтением сообщения из буфера и обработкой сообщения.