Самодел 2 (1114717), страница 15
Текст из файла (страница 15)
#include <sys/ipc.h>
#include <sys/msg.h>
int main(int argc, chr **argv)
{
struct {
long mestype; /*описание структуры сообщения*/
long mes;
} messageto;
struct {
long mestype; /*описание структуры сообшения*/
char mes[100];
} messagefrom;
key_t key;
int mesid;
long pid=getpid();
key=ftok("example",'r');
mesid=msgget (key,0 ); /*присоединение к очереди сообщений*/
messageto.mestype=1;
messageto.mes=pid;
msgsnd (mesid, &messageto, sizeof(messageto), 0); /*посылка */
while ( msgrcv (mesid, &messagefrom, sizeof(messagefrom), pid, 0)<=0);
/*прием собщения */
printf("%s",messagefrom.mes);
return 0;
}
IPC: разделяемая память.
Механизм разделяемой памяти позволяет нескольким процессам получить отображение некоторых страниц из своей виртуальной памяти на общую область физической памяти. Благодаря этому, данные, находящиеся в этой области памяти, будут доступны для чтения и модификации всем процессам, подключившимся к данной области памяти.
Процесс, подключившийся к разделяемой памяти, может затем получить указатель на некоторый адрес в своем виртуальном адресном пространстве, соответствующий данной области разделяемой памяти. После этого он может работать с этой областью памяти аналогично тому, как если бы она была выделена динамически (например, путем обращения к malloc()), однако, как уже говорилось, разделяемая область памяти не уничтожается автоматически даже после того, как процесс, создавший или использовавший ее, перестанет с ней работать.
Рассмотрим набор системных вызовов для работы с разделяемой памятью.
Создание общей памяти.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget (key_t key, int size, int shmemflg)
key – ключ для доступа к разделяемой памяти
size – размер области памяти. Если в результате вызова shmget() будет создана новая область разделяемой памяти, то ее размер будет соответствовать значению size. Если же процесс подключается к существующей области разделяемой памяти, то значение size должно быть не более ее размера, иначе вызов вернет –1.
shmeflg – флаги управляющие поведением вызова
Заметим, что если процесс при подключении к существующей области разделяемой памяти указал в аргументе size значение, меньшее ее фактического размера, то впоследствии он сможет получить доступ только к первым size байтам этой области.
Подробнее алгоритм создания/подключения разделяемого ресурса был описан выше.
В случае успешного завершения вызов возвращает положительное число – дескриптор области памяти, в случае неудачи - -1.
Доступ к разделяемой памяти.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
char *shmat(int shmid, char *shmaddr, int shmflg)
shmid – дескриптор области памяти
shmaddr – виртуальный адрес в адресном пространстве, начиная с которого необходимо подсоединить разделяемую память (чаще всего =0, то есть выбор редоставляется системе). Передача конкретного адреса в этом параметре имеет смысл в том случае, если, к примеру, в разделяемую память записываются указатели на нее же (например, в ней хранится связанный список) – в этой ситуации для того, чтобы использование этих указателей имело смысл и было корректным для всех процессов, подключенных к памяти, важно, чтобы во всех процессах адрес начала области разделяемой памяти совпадал.
shmflg – комбинация флагов, например, SHM_RDONLY - подсоединяемая область будет использоваться только для чтения.
При помощи этого вызова процесс подсоединяет область разделяемой памяти, дескриптор которой указан в shmid, к своему виртуальному адресному пространству. После выполнения этой операции процесс сможет читать и модифицировать данные, находящиеся в области разделяемой памяти, адресуя ее как любую другую область в своем собственном виртуальном адресном пространстве.
Эта функция возвращает адрес, начиная с которого будет отображаться присоединяемая разделяемая память. В случае неудачи вызов возвращает -1.
Открепление разделяемой памяти.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmdt(char *shmaddr)
shmaddr - адрес прикрепленной к процессу памяти, который был получен при вызове shmat()
Данный вызов позволяет отсоединить разделяемую память, ранее присоединенную посредством вызова shmat()
В случае успешного выполнения функция возвращает 0, в случае неудачи -1
Управление разделяемой памятью.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf)
shmid – дескриптор области памяти
cmd – IPC_STAT – скопировать структуру, описывающую управляющие параметры области памяти по адресу, указанному в параметре buf
IPC_SET – заменить структуру, описывающую управляющие параметры области памяти, на структуру, находящуюся по адресу, указанному в параметре buf. Выполнить эту операцию может процесс, у которого эффективный идентификатор пользователя совпадает с владельцем или создателем очереди, либо процесс с правами привилегированного пользователя, при этом процесс может изменить только владельца области памяти и права доступа к ней.
IPC_RMID – удалить очередь. Как уже говорилось, удалить очередь может только процесс, у которого эффективный идентификатор пользователя совпадает с владельцем или создателем очереди, либо процесс с правами привилегированного пользователя.
SHM_LOCK, SHM_UNLOCK – блокировать или разблокировать область памяти. Выполнить эту операцию может только процесс с правами привилегированного пользователя.
buf – структура, описывающая управляющие параметры области памяти. (Тип shmid_ds описан в заголовочном файле <sys/shm.h>, и представляет собой структуру, в полях которой хранятся права доступа к области памяти, ее размер, число процессов, подсоединенных к ней в данный момент, и статистика обращений к области памяти.)
Данный вызов используется для получения или изменения процессом управляющих параметров, связанных с областью разделяемой памяти, наложения и снятия блокировки на нее и ее уничтожения. области памяти.
Пример. Работа с общей памятью в рамках одного процесса.
int main(int argc, chr **argv)
{
key_t key;
char *shmaddr;
key=ftok(“/tmp/ter”,’S’);
shmid=shmget(key, 100,0666|IPC_CREAT);
shmaddr=shmat(shmid,NULL,0); /*подключение к памяти*/
putm(shmaddr); /*работа с ресурсом*/
waitprocess();
shmctl(shmid,IPC_RMID,NULL); /*уничтожение ресурса*/
exit();
}
IPC: массив семафоров.
Семафоры представляют собой одну из форм IPC и используются для синхронизации доступа нескольких процессов к разделяемым ресурсам, т.е. фактически они разрешают или запрещают процессу использование разделяемого ресурса. В начале излагалась идея использования такого механизма. Речь шла о том, что при наличии некоторого разделяемого ресурса , с которым один из процессов работает, необходимо блокировать доступ к нему других процессов. Для этого с ресурсом связывается некоторая переменная-счетчик, доступная для всех процессов. При этом считаем, что значение счетчика, равное 1 будет означать доступность ресурса, а значение, равное 0 — его занятость. Далее работа организуется следующим образом: процесс, которому необходим доступ к файлу, проверяет значение счетчика, если оно равно 0, то он в цикле ожидает освобождения ресурса, если же оно равно 1, процесс устанавливает значение счетчика равным 0 и работает с ресурсом. После завершения работы необходимо открыть доступ к ресурсу другим процессам, поэтому снова сбрасывается значение счетчика на 1. В данном примере счетчик и есть семафор.
Семафор находится адресном пространстве ядра и все операции выполняются также в режиме ядра.
В System V IPC семафор представляет собой группу (вектор) счетчиков, значения которых могут быть произвольными в пределах, определенных системой (не только 0 и 1).
Схема использования семафоров
•
С каждым разделяемым ресурсом связывается один семафор из набора
• Значение >0 – ресурс свободен,
<0 – ресурс занят
• Перед обращением к ресурсу процесс уменьшает значение соответствующего семафора
• Закончив работу с ресурсом, процесс увеличивает значение семафора
• В случае реализации взаимного исключения используется двоичный семафор.
Доступ к семафору
Для получения доступа (или его создания) к семафору используется системный вызов:
#include <sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
int semget (key_t key, int nsems, int semflag).
key – ключ
sems – количество семафоров (длина массива семафоров)
semflag – флаги, определяющие права доступа и те операции, которые должны выполняться (открытие семафора, проверка, и т.д.).
Эта функция возвращает целочисленный идентификатор созданного разделяемого ресурса, либо -1, если ресурс не удалось создать.
Операции над семафором
C полученным идентификатором созданного объекта можно производить операции с семафором, для чего используется системный вызов semop():
int semop (int semid, struct sembuf *semop, size_t nops)
semid – идентификатор ресурса
semop – указатель на структуру, определяющую операции, которые нужно призвести над семафором
nops– количество указателей на эту структуру, которые передаются функцией semop() (операций может быть несколько и операционная система гарантирует их атомарное выполнение).
Структура sembuf имеет вид:
struct sembuf
{
short sem_num; /*номер семафора в векторе*/
short sem_op; /*производимая операция*/
short sem_flg; /*флаги операции*/
}
Поле операции интерпретируется следующим образом. Пусть значение семафора с номером sem_num равно sem_val. В этом случае, если значение операции не равно нулю, то оценивается значение суммы sem_val + sem_op. Если эта сумма больше либо равна нулю, то значение данного семафора устанавливается равным сумме предыдущего значения и кода операции, т.е. sem_val:= sem_val+sem_op. Если эта сумма меньше нуля, то действие процесса будет приостановлено до наступления одного из следующих событий:
1. Значение суммы sem_val + sem_op станет больше либо равно нулю.
2. Пришел какой-то сигнал. Значение semop в этом случае будет равно -1.
Если код операции semop равен нулю, то процесс будет ожидать обнуления семафора. Если мы обратились к функции semop с нулевым кодом операции, а к этому моменту значение семафора стало равным нулю, то никакого ожидания не происходит.
Рассмотрим третий параметр - флаги. Если третий параметр равен нулю, то это означает, что флаги не используются. Флагов имеется большое количество в т.ч. IPC_NOWAIT (при этом флаге во всех тех случаях, когда мы говорили, что процесс будет ожидать, он не будет ожидать).
Управление массивом семафоров
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl (int semid, int num, int cmd, union semun arg)
semid – дескриптор массива семафоров
num – индекс семафора в массиве
cmd – операция
IPC_SET заменить управляющие наборы семафоров на те, которые указаны в arg.buf
IPC_RMID удалить массив семафоров и др.
arg – управляющие параметры
Третий аргумент:
(<sys/sem.h>)
union semun {
int val; /* значение одного семафора */
struct semid_ds *buf; /* параметры массива семафоров в целом (количество,
права доступа, статистика доступа)*/
ushort *array; /* массив значений семафоров */
}
Возвращает значение, соответствующее выполнявшейся операции (по умолчанию 0), в случае неудачи – -1
Пример. Использование разделяемой памяти и семафоров.
Рассмотрим двухпроцессную программу:
1 процесс - создает ресурсы “разделяемая память” и “семафоры”, далее он начинает принимать строки со стандартного ввода и записывает их в разделяемую память.
2 процесс - читает строки из разделяемой памяти.
1й процесс:
#include <stdio.h>