Вордовские лекции (1115151), страница 16
Текст из файла (страница 16)
В случае удачного чтения возвращаемое значение вызова равно 0.
Управление очередью сообщений.
Функция управления очередью сообщений выглядит следующим образом:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl (int msqid, int cmd, struct msgid_ds *buf)
Данный вызов используется для получения или изменения процессом управляющих параметров, связанных с очередью и уничтожения очереди. Ее аргументы — идентификатор ресурса, команда, которую необходимо выполнить, и структура, описывающая управляющие параметры очереди. Тип msgid_ds описан в заголовочном файле <sys/message.h>, и представляет собой структуру, в полях которой хранятся права доступа к очереди, статистика обращений к очереди, ее размер и т.п.
Возможные значения аргумента cmd:
IPC_STAT – скопировать структуру, описывающую управляющие параметры очереди по адресу, указанному в параметре buf
IPC_SET – заменить структуру, описывающую управляющие параметры очереди, на структуру, находящуюся по адресу, указанному в параметре buf
IPC_RMID – удалить очередь. Как уже говорилось, удалить очередь может только процесс, у которого эффективный идентификатор пользователя совпадает с владельцем или создателем очереди, либо процесс с правами привилегированного пользователя.
Пример. Использование очереди сообщений.
Пример программы, где основной процесс читает некоторую текстовую строку из стандартного ввода и в случае, если строка начинается с буквы 'a', то эта строка в качестве сообщения будет передана процессу А, если 'b' - процессу В, если 'q' - то процессам А и В, затем будет осуществлен выход. Процессы А и В распечатывают полученные строки на стандартный вывод.
6.3.2.1.1.1.1.1Основной процесс.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/message.h>
#include <stdio.h>
struct {long mtype; /* тип сообщения */
char Data[256]; /* сообщение */
} Message;
int main(int argc, chr **argv)
{
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(int argc, chr **argv)
{
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*/
printf("%s",Message.Data);
if (Message.Data[0]='q' || Message.Data[0]='Q') break;
}
exit();
}
Благодаря наличию типизации сообщений, очередь сообщений предоставляет возможность мультиплексировать сообщения от различных процессов, при этом каждая пара взаимодействующих через очередь процессов может использовать свой тип сообщений, и таким образом, их данные не будут смешиваться.
В качестве иллюстрации приведем следующий стандартный пример взаимодействия. Рассмотрим еще один пример - пусть существует процесс-сервер и несколько процессов-клиентов. Все они могут обмениваться данными, используя одну очередь сообщений. Для этого сообщениям, направляемым от клиента к серверу, присваиваем значение типа 1. При этом процесс, отправивший сообщение, в его теле передает некоторую информацию, позволяющую его однозначно идентифицировать. Тогда сервер, отправляя сообщение конкретному процессу, в качестве его типа указывает эту информацию (например, PID процесса). Таким образом, сервер будет читать из очереди только сообщения типа 1, а клиенты — сообщения с типами, равными идентификаторам их процессов.
Пример. Очередь сообщений.
Модель «клиент-сервер»
server
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, chr **argv)
{
struct {
long mestype;
char mes [100];
} messageto;
struct {
long mestype;
long mes;
} messagefrom;
key_t key;
int mesid;
key=ftok("example",'r');
mesid=msgget (key, 0666 | IPC_CREAT);
while(1)
{
if (msgrcv(mesid, &messagefrom, sizeof(messagefrom), 1, 0)<=0) continue;
messageto.mestype=messagefrom.mes;
strcpy( messageto.mes, "Message for client");
msgsnd (mesid, &messageto, sizeof(messageto), 0);
}
msgctl (mesid, IPC_RMID, 0);
return 0;
}
client
#include <sys/types.h>
#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;
}
6.3.3IPC: разделяемая память.
Механизм разделяемой памяти позволяет нескольким процессам получить отображение некоторых страниц из своей виртуальной памяти на общую область физической памяти. Благодаря этому, данные, находящиеся в этой области памяти, будут доступны для чтения и модификации всем процессам, подключившимся к данной области памяти.
Процесс, подключившийся к разделяемой памяти, может затем получить указатель на некоторый адрес в своем виртуальном адресном пространстве, соответствующий данной области разделяемой памяти. После этого он может работать с этой областью памяти аналогично тому, как если бы она была выделена динамически (например, путем обращения к 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. Заметим, что если процесс при подключении к существующей области разделяемой памяти указал в аргументе size значение, меньшее ее фактического размера, то впоследствии он сможет получить доступ только к первым size байтам этой области.
Третий параметр определяет флаги, управляющие поведением вызова. Подробнее алгоритм создания/подключения разделяемого ресурса был описан выше.
В случае успешного завершения вызов возвращает положительное число – дескриптор области памяти, в случае неудачи - -1.
Доступ к разделяемой памяти.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
char *shmat(int shmid, char *shmaddr, int shmflg)
При помощи этого вызова процесс подсоединяет область разделяемой памяти, дескриптор которой указан в shmid, к своему виртуальному адресному пространству. После выполнения этой операции процесс сможет читать и модифицировать данные, находящиеся в области разделяемой памяти, адресуя ее как любую другую область в своем собственном виртуальном адресном пространстве.
В качестве второго аргумента процесс может указать виртуальный адрес в своем адресном пространстве, начиная с которого необходимо подсоединить разделяемую память. Чаще всего, однако, в качестве значения этого аргумента передается 0, что означает, что система сама может выбрать адрес начала разделяемой памяти. Передача конкретного адреса в этом параметре имеет смысл в том случае, если, к примеру, в разделяемую память записываются указатели на нее же (например, в ней хранится связанный список) – в этой ситуации для того, чтобы использование этих указателей имело смысл и было корректным для всех процессов, подключенных к памяти, важно, чтобы во всех процессах адрес начала области разделяемой памяти совпадал.
Третий аргумент представляет собой комбинацию флагов. В качестве значения этого аргумента может быть указан флаг SHM_RDONLY, который указывает на то, что подсоединяемая область будет использоваться только для чтения.
Эта функция возвращает адрес, начиная с которого будет отображаться присоединяемая разделяемая память. В случае неудачи вызов возвращает -1.
Открепление разделяемой памяти.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmdt(char *shmaddr)
Данный вызов позволяет отсоединить разделяемую память, ранее присоединенную посредством вызова shmat()
shmaddr - адрес прикрепленной к процессу памяти, который был получен при вызове 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_ds описан в заголовочном файле <sys/shm.h>, и представляет собой структуру, в полях которой хранятся права доступа к области памяти, ее размер, число процессов, подсоединенных к ней в данный момент, и статистика обращений к области памяти.
Возможные значения аргумента 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’);