Interprocess Communication

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

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

Следующий механизм, который мы с вами рассмотрели - очередь сообщений. Имеется возможность совместной работы с разделяемым объектом, который называется очередь сообщений. Имеется сообщение, которое состоит из некоторого спецификатора типа, и некоторого набора данных. Процесс, подтвердив ключ и имея права доступа к этому разделяемому ресурсу, может осуществлять действия по записи сообщений в очередь, и по чтению сообщений из очереди.

Порядок чтения и записи сообщений из очереди соответствует названию этой структуры - очередь. Кроме того, за счет того, что каждое сообщение типизировано, есть возможность рассмотрения этой очереди с нескольких точек зрения. Первая точка зрения - это одна очередь и порядок в ней хронологический. Вторая точка зрения - это возможность представление этой очереди в виде нескольких очередей, каждая из которых содержит элементы определенного типа.

Понятно, что механизм сообщений может выступать в двух ролях: как средство передача данных, и как средство синхронизации (понятно каким образом).

Итак, к сегодняшнему дню мы познакомились с двумя этими механизмами. Напомню, как только мы переходим к работе от однопроцессной задачи к задаче многопроцессной, у нас сразу же возникают проблемы, связанные с тем, что любой параллелизм накладывает определенную ответственность на программу. Это ответственность по синхронизации доступа к разделяемой памяти, ответственность за правильность подпрограммы, занимающейся приемом и передачей сообщений и т.д. Можно, например, ошибиться в механизме передачи и приема сообщений за счет того, что какой-то процесс будет бесконечно долго ожидать несуществующее сообщение, то, которое никогда в очереди не появится, и система вам никогда такую ошибку не зафиксирует. Т.е. возможны зависания процессов, могут образоваться неосвобожденные ресурсы ("мусор"), и это приводит к деградации системы.

Сейчас мы напишем следующую программу: первый процесс будет читать некоторую текстовую строку из стандартного ввода и в случае, если строка начинается с буквы 'a', то эта строка в качестве сообщения будет передана процессу А, если 'b' - процессу В, если 'q' - то процессам А и В и затем будет осуществлен выход. Процессы А и В распечатывают полученные строки на стандартный вывод.

Основной процесс

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/message.h>

#include <stdio.h>

struct { long mtype; /* тип сообщения */

char Data[256]; /* сообщение */

} Message;

int main()

{ key_t key; int msgid; char str[256];

key=ftok("/usr/mash",'s'); /*получаем уникальный ключ, однозначно определяющий доступ к ресурсу данного типа */

msgid=msgget(key, 0666 | IPC_CREAT); /*создаем очередь сообщений , 0666 определяет права доступа */

for(;;) { /* запускаем вечный цикл */

gets(str); /* читаем из стандартного ввода строку */

strcpy(Message.Data, str); /* и копируем ее в буфер сообщения */

switch(str[0]){

case 'a':

case 'A': Message.mtype=1; /* устанавливаем тип и посылаем сообщение в очередь*/

msgsnd(msgid, (struct msgbuf*) (&Message), strlen(str)+1, 0);

break;

case 'b':

case 'B': Message.mtype=2;

msgsnd(msgid, (struct msgbuf*) (&Message), strlen(str)+1, 0);

break;

case q':

case 'Q': Message.mtype=1;

msgsnd(msgid, (struct msgbuf*) (&Message), strlen(str)+1, 0);

Message.mtype=2;

msgsnd(msgid, (struct msgbuf*) (&Message), strlen(str)+1, 0);

sleep(10); /* ждем получения сообщений процессами А и В */

msgctl(msgid, IPC_RMID, NULL); /* уничтожаем очередь*/

exit(0);

default: break;

}

}

}

Процесс-приемник А /* процесс В аналогичен с точностью до четвертого параметра в msgrcv */

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/message.h>

#include <stdio.h>

struct { long mtype;

char Data[256];

} Message;

int main()

{ key_t key; int msgid;

key=ftok("/usr/mash",'s'); /* получаем ключ по тем же параметрам */

msgid=msgget(key, 0666 | IPC_CREAT); /*создаем очередь сообщений */

for(;;) { /* запускаем вечный цикл */

msgrcv(msgid, (struct msgbuf*) (&Message), 256, 1, 0); /* читаем сообщение с типом 1*/

if (Message.Data[0]='q' || Message.Data[0]='Q') break;

printf("%s",Message.Data);

}

exit(0);

}

Семафоры

С точки зрения тех проблем, с которыми мы знакомимся, семафоры - это есть вполне законное и существующее понятие. Впервые ввел это понятие достаточно известный ученый Дейкстра. Семафор - это некоторый объект, который имеет целочисленное значение S, и с которым связаны две операции: V(S) и P(S).

Операция P(S) уменьшает значение семафора на 1, и если S³0 процесс продолжает работу. Если S<0, то процесс будет приостановлен и встанет в очередь ожидания, связанную с семафором S, до тех пор, пока его не освободит другой процесс.

Операция V(S) увеличивает семафор на 1. Если S>0, то процесс продолжает выполнение. Если S£0, то разблокируется один из процессов, ожидающий в очереди процессов, связанной с семафором S, и текущий процесс продолжит выполнение.

Считается, что операции P(S) и V(S) неделимы. Это означает, что выполнение этих операций не может прерваться до их завершения. Т.е. если семафор реализован в системе, то это должна быть одна команда.

Частным случаем продекларированного семафора является двоичный семафор, максимальное значение которого равно единичке. При этом значение S может быть равно 1, это означает, что ни один из процессов (связанных с этим семафором) не находится в критическом участке. При S=0 один из процессов находится в критическом участке {вот-вот попадет в очередь}, а другой нормально функционирует. При S= -1 один из процессов находится в критическом участке, а другой заблокирован и находится в очереди.

На самом деле двоичные семафоры наиболее часто находили применение в аппаратных реализациях, например, в многомашинных комплексах с общей оперативной памятью.


Страница: