Пример передачи сообщений: очередь с приоритетами

В этом разделе разработаем простое приложение для передачи сообщений. Его целью является реализация очереди, в которой для каждого элемента может быть задан приоритет. Серверный процесс будет выбирать элементы из очереди и об­рабатывать их каким-либо образом. Например, элементы очереди могут быть име­нами файлов, а серверный процесс может копировать их на принтер. Этот пример аналогичен примеру использования FIFO.

Отправной точкой будет следующий заголовочный файл q.h:

/* q.h – заголовок для примера очереди сообщений */

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/msg.h>

#include <string.h>

#include <errno.h>

#define QKEY (key_t) 0105 /* Ключ очереди */

#define QPERM 0660 /* Права доступа */

#define MAXOBN 50 /* Макс. длина имени объекта */

#define MAXPRIOR 10 /* Максимальный приоритет */

struct q_entry

{

long mtype;

char mtext [MAXOBN+1];

};

Первая часть этого файла содержит необходимые включаемые файлы. Определение QKEY задает значение ключа, которое будет обозначать очередь сообщений в системе. Определение QPERM устанавливает связанные с очередью права доступа. Так как код доступа равен 0660, то владелец очереди и члены его группы смогут выполнять чтение и запись. Как увидим позже, определения MAXOBN и MAXPRIOR будут налагать ограничения на сообщения, помещаемые в очередь. Последняя часть этого включаемого файла содержит определение структуры q_entry. Структуры этого типа будут использоваться в качестве сообщений, передаваемых и принимаемых следующими процедурами.

Первая рассматриваемая процедура называется enter, она помещает в очередь имя объекта, заканчивающееся нулевым символом, и имеет следующую форму:

/* Процедура enter – поместить объект в очередь */

#include “q.h”

int enter (char *objname, int priority)

{

int len, s_qid;

struct q_entry s_entry; /* Структура для хранения сообщений */

/* Проверка длины имени и уровня приоритета */

if((len = strlen(objname)) > MAXOBN)

{

warn (“ Слишком длинное имя “);

return (-1);

}

if (priority > MAXPRIOR || priority <0)

{

warn (“ Недопустимый уровень приоритета ”);

return (-1);

}

/* Инициализация очереди сообщений, если это необходимо */

if((s_qid = init_queue()) == -1)

return (-1);

/* Инициализация структуры s_entry */

s_entry.mtype = (long)priority;

strcpy (s_entry.mtext, objname, MAXOBN);

/* Посылаем сообщение, выполнив ожидание, если это необходимо*/

if (msgsnd(s_qid, &s_entry, len, 0) == -1)

{

perror(“Ошибка вызова msgsnd”);

return (-1);

}

else

return (0);

}

Первое действие, выполняемое процедурой enter, заключается в проверке длины имени объекта и уровня приоритета. Обратите внимание на то, что мини­мальное значение переменной приоритета priority равно 1, так как нулевое зна­чение приведет к неудачному завершению вызова msgsnd. Затем процедура enter «открывает» очередь, вызывая процедуру init_gueue, реализацию которой при­ведем позже.

После завершения этих действий процедура формирует сообщение и пытается послать его при помощи вызова msgsnd. Здесь для хранения сообщения исполь­зована структура s_entry типа q_entry, и последний параметр вызова msgsnd равен нулю. Это означает, что система приостановит выполнение текущего про­цесса, если очередь заполнена (так как не задан флаг IPC_NOWAIT ).

Процедура enter сообщает о возникших проблемах при помощи функции warn или библиотечной функции perror. Для простоты функция warn реализо­вана следующим образом:

#include <stdio.h>

int warn(char *s)

{

fprintf (stderr, "Предупреждение: %s\n", s);

}

В реальных системах функция warn должна записывать сообщения в специ­альный файл протокола.

Назначение функции init_queue очевидно. Она инициализирует идентифи­катор очереди сообщений или возвращает идентификатор очереди сообщений, ко­торый с ней уже связан.

/* Инициализация очереди — получить идентификатор очереди */

#include "q.h"

int init_queue(void)

{

int queue_id;

/* Попытка создания или открытия очереди сообщений */

if ((queue_id = msgget(QKEY, IPC_CREAT | QPERM)) == -1)

perror(“ Ошибка вызова msgget”);

return (queue_id);

}

Следующая процедура, serve, используется серверным процессом для получения сообщений из очереди и противоположна процедуре enter.

/* Процедура serve — принимает и обрабатывает сообщение очереди с наивысшим приоритетом */

#include "q.h"

int serve(void)

{

int mlen, r_qid;

struct q_entry r_entry;

/* Инициализация очереди сообщений, если это необходимо */

if((r_qid = init_queue()) == -1)

return (-1);

/* Получить и обработать следующее сообщение */

for(;;)

{

if((mlen = msgrcv(r_qid, &r_entry, MAXOBN,

(-1 * MAXPRIOR), MSG_NOERROR)) == -1)

{

perror("Ошибка вызова msgrcv");

return (-1);

}

else

{

/* Убедиться, что это строка */

r_entry.mtext[mlen]='\0';

/* Обработать имя объекта */

proc_obj(&r_entry);}

}

}

Обратите внимание на вызов msgrcv. Так как в качестве параметра типа зада­но отрицательное значение (-1 * MAXPRIOR), то система вначале проверяет оче­редь на наличие сообщений со значением mtype равным 1, затем равным 2 и так далее, до значения MAXPRIOR включительно. Другими словами, сообщения с наи­меньшим номером будут иметь наивысший приоритет. Процедура proc_obj работает с объектом. Для системы печати она может просто копировать файл на принтер.

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

Программа etest

/* Программа etest - ввод имен объектов в очередь */

#include <stdio.h>

#include <stdlib.h>

#include "q.h"

main(int argc, char **argv)

{

int priority;

if(argc!= 3)

{

fprintf(stderr, "Применение: %s имя приоритет\n, argv[0]);

exit(1);

}

if((priority = atoi(argv[2])) <= 0 || priority > MAXPRIOR)

{

warn("Недопустимый приоритет");

exit(2);

}

if(enter(argv[1], priority) < 0)

{

warn("Ошибка в процедуре enter");

exit(3);

}

exit(0);

}

Программа stest

/* Программа stest - простой сервер для очереди */

#include <stdio.h>

#include "q.h"

main()

{

pid_t pid;

switch(pid = fork())

{

case 0: /* Дочерний процесс */

serve();

break; /* Сервер не существует */

case -1:

warn(“Не удалось запустить сервер”);

break;

default:

printf(“Серверный процесс с идентификатором %d\n”, pid);

}

exit(pid!= -1? 0: 1);

}

int proc_obj(struct q_entry *msg)

{

printf(“\nПриоритет: %ld имя: %s\n”, msg->mtype, msg->mtext);

}

Ниже следует пример использования этих двух простых программ. Перед за­пуском программы stest в очередь вводятся четыре простых сообщения при по­мощи программы etest. Обратите внимание на порядок, в котором выводятся сообщения:

$ etest objname1 3

$ etest objname2 4

$ etest objname3 1

$ etest objname4 9

$ etest

Серверный процесс с идентификатором 2545

$

Приоритет 1 имя objname3

Приоритет 3 имя objname1

Приоритет 4 имя objname2

Приоритет 9 имя objname4


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: