Самодел 2003 (1114718), страница 14
Текст из файла (страница 14)
msgsnd(msgid, (struct msgbuf*) (&Message), strlen(str)+1, 0);
exit(0);
default: exit(0);
}
}
}
Процесс-приемник А
// процесс В аналогичен с точностью до четвертого параметра в 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);
// читаем сообщение с типом
if (Message.Data[0]='q' || Message.Data[0]='Q') exit(0);
printf("%s",Message.Data);
}
exit(0);
}
IPC: разделяемая память.
Механизм разделяемой памяти позволяет нескольким процессам получить отображение некоторых страниц из своей виртуальной памяти на общую область физической памяти. Благодаря этому, данные, находящиеся в этой области памяти, будут доступны для чтения и модификации всем процессам, подключившимся к данной области памяти.
Процесс, подключившийся к разделяемой памяти, может затем получить указатель на некоторый адрес в своем виртуальном адресном пространстве, соответствующий данной области разделяемой памяти. После этого он может работать с этой областью памяти аналогично тому, как если бы она была выделена динамически (например, путем обращения к malloc()), однако, как уже говорилось, разделяемая область памяти не уничтожается автоматически даже после того, как процесс, создавший или использовавший ее, перестанет с ней работать.
Рассмотрим набор системных вызовов для работы с разделяемой памятью.
1) Создание общей памяти.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget (key_t key, int size, int shmemflg)
где
ultulu key - ключ для доступа к разделяемой памяти;
size задает размер области памяти, к которой процесс желает получить доступ. Если в результате вызова shmget() будет создана новая область разделяемой памяти, то ее размер будет соответствовать значению size. Если же процесс подключается к существующей области разделяемой памяти, то значение size должно быть не более ее размера, иначе вызов вернет –1. Заметим, что если процесс при подключении к существующей области разделяемой памяти указал в аргументе size значение, меньшее ее фактического размера, то впоследствии он сможет получить доступ только к первым size байтам этой области.
shmemflg определяет флаги, управляющие поведением вызова.
В случае успешного завершения вызов возвращает положительное число – дескриптор области памяти, в случае неудачи - -1.
2) Доступ к разделяемой памяти.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
char *shmat(int shmid, char *shmaddr, int shmflg)
где в shmid указан дескриптор.
При помощи этого вызова процесс подсоединяет область разделяемой памяти, дескриптор которой указан в shmid, к своему виртуальному адресному пространству. После выполнения этой операции процесс сможет читать и модифицировать данные, находящиеся в области разделяемой памяти, адресуя ее как любую другую область в своем собственном виртуальном адресном пространстве.
*shmaddr виртуальный адрес в адресном пространстве, начиная с которого необходимо подсоединить разделяемую память. Чаще всего, однако, в качестве значения этого аргумента передается 0, что означает, что система сама может выбрать адрес начала разделяемой памяти. Передача конкретного адреса в этом параметре имеет смысл в том случае, если, к примеру, в разделяемую память записываются указатели на нее же (например, в ней хранится связанный список) – в этой ситуации для того, чтобы использование этих указателей имело смысл и было корректным для всех процессов, подключенных к памяти, важно, чтобы во всех процессах адрес начала области разделяемой памяти совпадал.
Shmflg - комбинация флагов. В качестве значения этого аргумента может быть указан флаг SHM_RDONLY, который указывает на то, что подсоединяемая область будет использоваться только для чтения.
Эта функция возвращает адрес, начиная с которого будет отображаться присоединяемая разделяемая память. В случае неудачи вызов возвращает -1.
2)
Открепление разделяемой памяти.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmdt(char *shmaddr)
Где shmaddr - адрес прикрепленной к процессу памяти, который был получен при вызове shmat()
Данный вызов позволяет отсоединить разделяемую память, ранее присоединенную посредством вызова shmat()
В случае успешного выполнения функция возвращает 0, в случае неудачи -1
3)
Управление разделяемой памятью.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf)
где -
shmid -
дескриптор области памяти, команда, которую необходимо выполнить, и структура, описывающая управляющие параметры области памяти.
cmd – команда. Возможные значения аргумента cmd:
IPC_STAT – скопировать структуру, описывающую управляющие параметры области памяти по адресу, указанному в параметре buf
IPC_SET – заменить структуру, описывающую управляющие параметры области памяти, на структуру, находящуюся по адресу, указанному в параметре buf. Выполнить эту операцию может процесс, у которого эффективный идентификатор пользователя совпадает с владельцем или создателем очереди, либо процесс с правами привилегированного пользователя, при этом процесс может изменить только владельца области памяти и права доступа к ней.
IPC_RMID – удалить очередь. Как уже говорилось, удалить очередь может только процесс, у которого эффективный идентификатор пользователя совпадает с владельцем или создателем очереди, либо процесс с правами привилегированного пользователя.
SHM_LOCK, SHM_UNLOCK – блокировать или разблокировать область памяти. Выполнить эту операцию может только процесс с правами привилегированного пользователя.
Данный вызов используется для получения или изменения процессом управляющих параметров, связанных с областью разделяемой памяти, наложения и снятия блокировки на нее и ее уничтожения.
Пример. Работа с общей памятью в рамках одного процесса.
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();
}
Массив семафоров
Семафоры используются для синхронизации доступа нескольких процессов к разделяемым ресурсам, т.е. фактически они разрешают или запрещают процессу использование разделяемого ресурса. В начале излагалась идея использования такого механизма. Речь шла о том, что при наличии некоторого разделяемого ресурса , с которым один из процессов работает, необходимо блокировать доступ к нему других процессов. Для этого с ресурсом связывается некоторая переменная-счетчик, доступная для всех процессов. При этом считаем, что значение счетчика, равное 1 будет означать доступность ресурса, а значение, равное 0 — его занятость. Далее работа организуется следующим образом: процесс, которому необходим доступ к файлу, проверяет значение счетчика, если оно равно 0, то он в цикле ожидает освобождения ресурса, если же оно равно 1, процесс устанавливает значение счетчика равным 0 и работает с ресурсом. После завершения работы необходимо открыть доступ к ресурсу другим процессам, поэтому снова сбрасывается значение счетчика на 1. В данном примере счетчик и есть семафор.
Семафор находится адресном пространстве ядра и все операции выполняются также в режиме ядра.
В System V IPC семафор представляет собой группу (вектор) счетчиков, значения которых могут быть произвольными в пределах, определенных системой (не только 0 и 1).
1) Доступ к семафору
#include <sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
int semget (key_t key, int nsems, int semflag).
где key - ключ,
nsems - количество семафоров (длина массива семафоров)
semflag - флаги. Через флаги можно определить права доступа и те операции, которые должны выполняться (открытие семафора, проверка, и т.д.).
Функция semget() возвращает целочисленный идентификатор созданного разделяемого ресурса, либо -1, если ресурс не удалось создать.
2)Операции над семафором
int semop (int semid, struct sembuf *semop, size_t nops)
где
semid – идентификатор ресурса,
*semop - указатель на структуру, определяющую операции, которые необходимо произвести над семафором.
struct sembuf { short sem_num; /*номер семафора в векторе*/
short sem_op; /*производимая операция*/
short sem_flg; /*флаги операции*/
}
nops - количество указателей на эту структуру, которые передаются функцией semop(). То есть операций может быть несколько и операционная система гарантирует их атомарное выполнение.
Поле операции интерпретируется следующим образом. Пусть значение семафора с номером 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 (при этом флаге во всех тех случаях, когда мы говорили, что процесс будет ожидать, он не будет ожидать).
Пример. Использование разделяемой памяти и семафоров.
Рассмотрим двухпроцессную программу:
1 процесс - создает ресурсы “разделяемая память” и “семафоры”, далее он начинает принимать строки со стандартного ввода и записывает их в разделяемую память.
2 процесс - читает строки из разделяемой памяти.
Таким образом, мы имеем критический участок в момент, когда один процесс еще не дописал строку, а другой ее уже читает. Поэтому следует установить некоторые синхронизации и задержки.
1й процесс:
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int main(void)
{ key_t key;
int semid, shmid;
struct sembuf sops;
char *shmaddr;
char str[256];
key = ftok(“/usr/mash/exmpl”,’S’); // создаем уникальный ключ
semid = semget(key,1,0666 | IPC_CREAT); // создаем один семафор с определенными // правами доступа
shmid = shmget(key,256, 0666 | IPC_CREAT); /*создаем разделяемую память на 256 элементов */
shmaddr = shmat(shmid, NULL, 0); /* подключаемся к разделу памяти, в shaddr -
указатель на буфер с разделяемой памятью*/
semctl(semid,0,IPC_SET, (union semun) 0); //инициализируем семафор со значением 0
sops.sem_num = 0; sops.sem_flg = 0;