Московский физико-технический институт
Опубликован: 16.09.2004 | Доступ: свободный | Студентов: 7773 / 1592 | Оценка: 4.44 / 4.28 | Длительность: 15:33:00
Лекция 6:

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

< Лекция 5 || Лекция 6: 1234 || Лекция 7 >

Удаление очереди сообщений из системы с помощью команды ipcrm или системного вызова msgctl()

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

ipcrm msg <IPC идентификатор>

Для получения IPC идентификатора очереди сообщений примените команду ipcs. Можно удалить очередь сообщений и с помощью системного вызова msgctl() . Этот вызов умеет выполнять и другие операции над очередью сообщений, но в рамках данного курса мы их рассматривать не будем. Если какой-либо процесс находился в состоянии ожидание при выполнении системного вызова msgrcv() или msgsnd() для удаляемой очереди, то он будет разблокирован, и системный вызов констатирует наличие ошибочной ситуации.

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

Прототип системного вызова

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgctl(int msqid, int cmd, 
           struct msqid_ds *buf);

Описание системного вызова

Системный вызов msgctl предназначен для получения информации об очереди сообщений, изменения ее атрибутов и удаления из системы. Данное описание не является полным описанием системного вызова, а ограничивается рамками текущего курса. Для изучения полного описания обращайтесь к UNIX Manual.

В нашем курсе мы будем пользоваться системным вызовом msgctl только для удаления очереди сообщений из системы. Параметр msqid является дескриптором System V IPC для очереди сообщений, т. е. значением, которое вернул системный вызов msgget() при создании очереди или при ее поиске по ключу.

В качестве параметра cmd в рамках нашего курса мы всегда будем передавать значение IPC_RMID – команду для удаления очереди сообщений с заданным идентификатором. Параметр buf для этой команды не используется, поэтому мы всегда будем подставлять туда значение NULL.

Возвращаемое значение

Системный вызов возвращает значение 0 при нормальном завершении и значение -1 при возникновении ошибки.

Прогон примера с однонаправленной передачей текстовой информации

Для иллюстрации сказанного рассмотрим две простые программы.

/* Программа 09-1a.c для иллюстрации работы с 
очередями сообщений */ 
/* Эта программа получает доступ к очереди 
сообщений, отправляет в нее 5 текстовых сообщений с типом 1 и одно
пустое сообщение с типом 255, которое будет служить для программы 
09-1b.c сигналом прекращения работы. */ 
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <stdio.h>
#define LAST_MESSAGE 255 /* Тип сообщения для 
    прекращения работы программы 09-1b.c */ 
int main()
{
    int msqid; /* IPC дескриптор для очереди сообщений */
char pathname[] = "09-1a.c"; /* Имя файла, 
        использующееся для генерации ключа. Файл с таким 
        именем должен существовать в текущей директории */
    key_t key; /* IPC ключ */ 
    int i,len; /* Счетчик цикла и длина 
        информативной части сообщения */
    /* Ниже следует пользовательская структура для 
        сообщения */
    struct mymsgbuf
    { 
        long mtype;
        char mtext[81]; 
    } mybuf;
    /* Генерируем IPC ключ из имени файла 09-1a.c в текущей 
    директории и номера экземпляра очереди сообщений 0. */
    if((key = ftok(pathname,0)) < 0){
        printf("Can\'t generate key\n");
        exit(-1);
    }
    /* Пытаемся получить доступ по ключу к очереди сообщений, 
    если она существует, или создать ее, с правами доступа 
    read & write для всех пользователей */ 
    if((msqid = msgget(key, 0666 | IPC_CREAT)) < 0){
        printf("Can\'t get msqid\n");
        exit(-1);
    } 
    /* Посылаем в цикле 5 сообщений с типом 1 
    в очередь сообщений, идентифицируемую msqid.*/
    for (i = 1; i <= 5; i++){
        /* Сначала заполняем структуру для нашего 
        сообщения и определяем длину информативной части */ 
        mybuf.mtype = 1;
        strcpy(mybuf.mtext, "This is text message");
        len = strlen(mybuf.mtext)+1;
        /* Отсылаем сообщение. В случае ошибки сообщаем об 
        этом и удаляем очередь сообщений из системы. */ 
        if (msgsnd(msqid, (struct msgbuf *) &mybuf, 
            len, 0) < 0){
            printf("Can\'t send message to queue\n");
            msgctl(msqid, IPC_RMID, 
                (struct msqid_ds *) NULL);
            exit(-1);
        }
    }
    /* Отсылаем сообщение, которое заставит получающий процесс
    прекратить работу, с типом LAST_MESSAGE и длиной 0 */ 
    mybuf.mtype = LAST_MESSAGE;
    len = 0;
    if (msgsnd(msqid, (struct msgbuf *) &mybuf, 
        len, 0) < 0){
        printf("Can\'t send message to queue\n");
        msgctl(msqid, IPC_RMID, 
            (struct msqid_ds *) NULL);
        exit(-1);
    }
    return 0;
}
Листинг 9.1a. Программа 09-1a.c для иллюстрации работы с очередями сообщений.
/* Программа 09-1b.c для иллюстрации работы с 
очередями сообщений */ 
/* Эта программа получает доступ к очереди сообщений и читает из
нее сообщения с любым типом в порядке FIFO до тех пор, пока не 
получит сообщение с типом 255, которое будет служить сигналом 
прекращения работы. */ 
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <stdio.h>
#define LAST_MESSAGE 255 /* Тип сообщения для 
    прекращения работы */ 
int main()
{
    int msqid; /* IPC дескриптор для очереди сообщений */
    char pathname[] = "09-1a.c"; /* Имя файла, 
        использующееся для генерации ключа. Файл с таким 
        именем должен существовать в текущей директории */
    key_t key; /* IPC ключ */ 
    int len, maxlen; /* Реальная длина и максимальная 
        длина информативной части сообщения */
    /* Ниже следует пользовательская структура для сообщения */
    struct mymsgbuf
    { 
        long mtype;
        char mtext[81]; 
    } mybuf;
    /* Генерируем IPC ключ из имени файла 09-1a.c в текущей 
    директории и номера экземпляра очереди сообщений 0 */
    if((key = ftok(pathname,0)) < 0){
        printf("Can\'t generate key\n");
        exit(-1);
    }
    /* Пытаемся получить доступ по ключу к очереди сообщений, 
    если она существует, или создать ее, с правами доступа 
    read & write для всех пользователей */ 
    if((msqid = msgget(key, 0666 | IPC_CREAT)) < 0){
        printf("Can\'t get msqid\n");
        exit(-1);
    } 
    while(1){
        /* В бесконечном цикле принимаем сообщения 
любого типа в порядке FIFO с максимальной длиной информативной 
части 81 символ до тех пор, пока не поступит сообщение с 
типом LAST_MESSAGE*/ 
        maxlen = 81;
        if(( len = msgrcv(msqid, 
            (struct msgbuf *) &mybuf, maxlen, 0, 0) < 0){
            printf("Can\'t receive message from queue\n");
            exit(-1);
        }
        /* Если принятое сообщение имеет тип LAST_MESSAGE, 
        прекращаем работу и удаляем очередь сообщений из 
        системы. В противном случае печатаем текст принятого 
        сообщения. */
        if (mybuf.mtype == LAST_MESSAGE){
            msgctl(msqid, IPC_RMID, 
            (struct msqid_ds *) NULL);
        exit(0);
        }
        printf("message type = %ld, info = %s\n", 
        mybuf.mtype, mybuf.mtext);
    }
    return 0; /* Исключительно для отсутствия 
        warning'ов при компиляции. */
}
Листинг 9.1b. Программа 09-1b.c для иллюстрации работы с очередями сообщений.

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

Обратите внимание на использование сообщения с типом 255 в качестве сигнала прекращения работы второго процесса. Это сообщение имеет нулевую длину, так как его информативность исчерпывается самим фактом наличия сообщения.

Наберите программы, сохраните под именами 09-1а.с и 09-1b.c cоответственно, откомпилируйте и проверьте правильность их поведения.

Модификация предыдущего примера для передачи числовой информации

В описании системных вызовов msgsnd() и msgrcv() говорится о том, что передаваемая информации не обязательно должна представлять собой текст.

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

struct mymsgbuf { 
    long mtype;
    struct { 
        short sinfo;
        float finfo;
    } info;
} mybuf;

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

sizeof(info)>=sizeof(short)+sizeof(float)

Для полной передачи информативной части сообщения в качестве длины нужно указывать не сумму длин полей, а полную длину структуры. Модифицируйте предыдущие программы 09-1a.c и 09-1b.c из раздела "Прогон примера с однонаправленной передачей текстовой информации" для передачи нетекстовых сообщений.

Написание, компиляция и прогон программ для осуществления двусторонней связи через одну очередь сообщений

Наличие у сообщений типов позволяет организовать двустороннюю связь между процессами через одну и ту же очередь сообщений. Процесс 1 может посылать процессу 2 сообщения с типом 1, а получать от него сообщения с типом 2. При этом для выборки сообщений в обоих процессах следует пользоваться вторым способом выбора (см. раздел "Очереди сообщений в UNIX как составная часть System V IPC"). Напишите, откомпилируйте и прогоните программы, осуществляющие двустороннюю связь через одну очередь сообщений.

< Лекция 5 || Лекция 6: 1234 || Лекция 7 >
лия логовина
лия логовина

организовать двустороннюю поочередную связь процесса-родителя и процесса-ребенка через pipe, используя для синхронизации сигналы sigusr1 и sigusr2.

Макар Оганесов
Макар Оганесов