Студопедия.Орг Главная | Случайная страница | Контакты | Мы поможем в написании вашей работы!  
 

Лабораторная работа № 24



Межпроцессное взаимодействие программ

Цель: освоение средств IPC. Написание программ, использующих механизм семафоров, очередей сообщений, сегментов разделяемой памяти.

Механизм IPC (Inter-Process Communication Facilities) включает:

· средства, обеспечивающие возможность синхронизации процессов при доступе к совместно используемым ресурсам (семафоры - semaphores);

· средства, обеспечивающие возможность посылки процессом сообщений другому произвольному процессу (очереди сообщений - message queries);

· средства, обеспечивающие возможность наличия общей для процессов памяти (сегменты разделяемой памяти - shared memory segments).

Наиболее общим понятием IPC является ключ, хранимый в общесистемной таблице и обозначающий объект межпроцессного взаимодействия, доступный нескольким процессам. Обозначаемый ключом объект может быть очередью сообщений, набором семафоров или сегментом разделяемой памяти. Ключ имеет тип key_t, состав которого зависит от реализации и определяется в файле sys/types.h. Ключ используется для создания объекта межпроцессного взаимодействия или получения доступа к существующему объекту. Обе операции выполняются посредством операции get. Результатом операции get является его целочисленный идентификатор, который может использоваться в других функциях межпроцессного взаимодействия.

Семафоры

Для работы с семафорами поддерживаются три системных вызова:

· semget() для создания и получения доступа к набору семафоров;

· semop() для манипулирования значениями семафоров (это тот системный вызов, который позволяет процессам синхронизироваться на основе использования семафоров);

· semctl() для выполнения разнообразных управляющих операций над набором семафоров.

Прототипы перечисленных системных вызовов описаны в файлах:

#include <sys/ipc.h>

#include <sys/sem.h>

Системный вызов semget() имеет следующий синтаксис:

semid = int semget(key_t key, int count, int flag);

Его параметрами являются ключ (key) набора семафоров и дополнительные флаги (flags), определенные в <sys/ipc.h>, число семафоров в наборе семафоров (count), обладающих одним и тем же ключом. Системный вызов возвращает идентификатор набора семафоров semid. После вызова semget() индивидуальный семафор идентифицируется идентификатором набора семафоров и номером семафора в этом наборе.

Флаги системного вызова semget() приведены в таблице:

IPC_CREAT semget() создает новый семафор для данного ключа. Если флаг IPC_CREAT не задан, а набор семафоров с указанным ключом уже существует, то обращающийся процесс получит идентификатор существующего набора семафоров.
IPC_EXLC Флаг IPC_EXLC вместе с флагом IPC_CREAT предназначен для создания (и только для создания) набора семафоров. Если набор семафоров уже существует, semget() возвратит -1, а системная переменная errno будет содержать значение EEXIST.

Младшие 9 бит флага задают права доступа к набору семафоров.

Системный вызов semctl() имеет формат:

int semctl (int semid, int sem_num, int command, union semun arg);

где semid - это идентификатор набора семафоров, sem_numb - номер семафора в группе, command - код операции, а arg - указатель на структуру, содержимое которой интерпретируется по-разному, в зависимости от операции.

Структура msg имеет вид:

union semun

{

int val;

struct semid_ds *buf;

unsigned short *array;

};

С помощью semctl() можно:

· уничтожить набор семафоров или индивидуальный семафор в указанной группе (IPC_RMID);

· вернуть значение отдельного семафора (GETVAL) или всех семафоров (GETALL);

· установить значение отдельного семафора (SETVAL) или всех семафоров (SETALL);

· вернуть число семафоров в наборе семафоров (GETPID).

Основным системным вызовом для манипулирования семафором является:

int semop (int semid, struct sembuf *op_array, count);

где semid - это ранее полученный дескриптор группы семафоров, op_array - массив структур sembuf, определенных в файле <sys/sem.h> и содержащих описания операций над семафорами группы, а count - размер этого массива. Значение, возвращаемое системным вызовом, является значением последнего обработанного семафора. Каждый элемент массива op_array имеет следующую структуру (структура sembuf):

· номер семафора в указанном наборе семафоров;

· операция над семафором;

· флаги.

Если указанные в массиве op_array номера семафоров не выходят за пределы общего размера набора семафоров, то системный вызов последовательно меняет значение семафора (если это возможно) в соответствии со значением поля "операция". Возможны три случая:

1. Отрицательное значение sem_op:

o если значение поля операции sem_op отрицательно, и его абсолютное значение меньше или равно значению семафора semval, то ядро прибавляет это отрицательное значение к значению семафора;

o если в результате значение семафора стало нулевым, то ядро активизирует все процессы, ожидающие нулевого значения этого семафора;

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

2. Положительное значение sem_op.

Если значение поля операции sem_op положительно, то оно прибавляется к значению семафора semval, а все процессы, ожидающие увеличения значения семафора, активизируются (пробуждаются в терминологии UNIX).

3. Нулевое значение sem_op:

o eсли значение поля операции sem_op равно нулю, то если значение семафора semval также равно нулю, выбирается следующий элемент массива op_array;

o если же значение семафора semval отлично от нуля, то ядро увеличивает на единицу число процессов, ожидающих нулевого значения семафора, а обратившийся процесс переводится в состояние ожидания.

При использовании флага IPC_NOWAIT ядро ОС UNIX не блокирует текущий процесс, а лишь сообщает в ответных параметрах о возникновении ситуации, приведшей бы к блокированию процесса при отсутствии флага IPC_NOWAIT.

Очереди сообщений

Для обеспечения возможности обмена сообщениями между процессами механизм очередей поддерживается следующими системными вызовами:

· msgget() для образования новой очереди сообщений или получения дескриптора существующей очереди;

· msgsnd() для постановки сообщения в указанную очередь сообщений;

· msgrcv() для выборки сообщения из очереди сообщений;

· msgctl() для выполнения ряда управляющих действий.

Прототипы перечисленных системных вызовов описаны в файлах:

#include <sys/ipc.h>

#include <sys/msg.h>

По системному вызову msgget() в ответ на ключ (key) и набор флагов (полностью аналогичны флагам в системном вызове semget()) ядро либо создает новую очередь сообщений и возвращает пользователю идентификатор созданной очереди, либо находит элемент таблицы очередей сообщений, содержащий указанный ключ, и возвращает соответствующий идентификатор очереди:

int msgqid = msgget(key_t key, int flag);

Для помещения сообщения в очередь служит системный вызов msgsnd():

int msgsnd (int msgqid, void *msg, size_t size, int flag);

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

Структура msg имеет вид:

struct msg

{

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

char mtext[SOMEVALUE]; /* текст сообщения (SOMEVALUE - любое) */

};

Параметр flag определяет действия ядра при выходе за пределы допустимых размеров внутренней буферной памяти (флаг IPC_NOWAIT со значением, рассмотренным выше).

Условиями успешной постановки сообщения в очередь являются:

· наличие прав процесса по записи в данную очередь сообщений;

· непревышение длиной сообщения заданного системой верхнего предела;

· положительное значение указанного в сообщении типа сообщения.

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

Для приема сообщения используется системный вызов msgrcv():

int msgrcv (int msgqid, void *msg, size_t size, long msg_type, int flag);

Системный вызов msgctl():

int msgctl (int msgqid, int command, struct msqid_ds *msg_stat);

используется:

· для опроса состояния описателя очереди сообщений (command = IPC_STAT) и помещения его в структуру msg_stat;

· изменения его состояния (command = IPC_SET), например, изменения прав доступа к очереди;

· для уничтожения указанной очереди сообщений (command = IPC_RMID).

Работа с разделяемой памятью

Для работы с разделяемой памятью используются системные вызовы:

· shmget() создает новый сегмент разделяемой памяти или находит существующий сегмент с тем же ключом;

· shmat() подключает сегмент с указанным описателем к виртуальной памяти обращающегося процесса;

· shmdt() отключает от виртуальной памяти ранее подключенный к ней сегмент с указанным виртуальным адресом начала;

· shmctl() служит для управления разнообразными параметрами, связанными с существующим сегментом.

Прототипы перечисленных системных вызовов описаны в файлах:

#include <sys/ipc.h>

#include <sys/shm.h>

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

Системный вызов

int shmid = shmget (key_t key, size_t size, int flag);

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

Подключение сегмента к виртуальной памяти выполняется путем обращения к системному вызову shmat():

void *virtaddr = shmat(int shmid, void *daddr, int flags);

Параметр shmid - это ранее полученный идентификатор сегмента, а daddr - желаемый процессом виртуальный адрес, который должен соответствовать началу сегмента в виртуальной памяти. Значением системного вызова является фактический виртуальный адрес начала сегмента. Если значением daddr является NULL, ядро выбирает наиболее удобный виртуальный адрес начала сегмента.

Флаги системного вызова shmat():

SHM_RDONLY ядро подключает участок памяти только для чтения.
SHM_RND определяет, если возможно, способ обработки ненулевого значения daddr.

Для отключения сегмента от виртуальной памяти используется системный вызов shmdt():

int shmdt(*daddr);

где daddr - это виртуальный адрес начала сегмента в виртуальной памяти, ранее полученный от системного вызова shmat().

Системный вызов shmctl()

int shmctl (int shmid, int command, struct shmid_ds *shm_stat);

по синтаксису и назначению полностью аналогичен msgctl().





Дата публикования: 2015-10-09; Прочитано: 304 | Нарушение авторского права страницы | Мы поможем в написании вашей работы!



studopedia.org - Студопедия.Орг - 2014-2024 год. Студопедия не является автором материалов, которые размещены. Но предоставляет возможность бесплатного использования (0.018 с)...