Скачиваний:
56
Добавлен:
08.01.2014
Размер:
2.6 Mб
Скачать

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

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

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

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

const

QKEY:tkey=(1 shl 6) + 5{0105}; (* ключ очереди *)

QPERM=(6 shl 6) + (6 shl 3){0660}; (* права доступа *)

MAXOBN=50; (* макс. длина имени объекта *)

MAXPRIOR=10; (* максимальный приоритет *)

type

q_entry=record

mtype:longint;

mtext:array [0..MAXOBN] of char;

end;

pq_entry=^q_entry;

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

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

{$i q.inc}

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

function enter (objname:string;priority:longint):boolean;

var

len, s_qid:longint;

s_entry:q_entry; (* структура для хранения сообщений *)

begin

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

len := length (objname);

if len > MAXOBN then

begin

warn ('слишком длинное имя');

enter:=false;

exit;

end;

if (priority > MAXPRIOR) or (priority < 0) then

begin

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

enter:=false;

exit;

end;

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

s_qid := init_queue;

if s_qid = -1 then

begin

enter:=false;

exit;

end;

(* Инициализация структуры переменной s_entry *)

s_entry.mtype := priority;

strlcopy (s_entry.mtext, @objname[1], MAXOBN);

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

if not msgsnd (s_qid, @s_entry, len, 0) then

begin

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

enter:=false;

exit;

end

else

enter:=true;

end;

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

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

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

procedure warn (s:pchar);

begin

writeln(stderr, 'Предупреждение: ', s);

end;

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

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

{$i q.inc}

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

function init_queue:longint;

var

queue_id:longint;

begin

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

queue_id := msgget (QKEY, IPC_CREAT or QPERM);

if queue_id = -1 then

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

init_queue:=queue_id;

end;

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

{$i q.inc}

(* Процедура serve - принимает и обрабатывает сообщение обслуживает

* объект очереди с наивысшим приоритетом

*)

function serve:integer;

var

r_qid:longint;

r_entry:q_entry;

begin

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

r_qid := init_queue;

if r_qid = -1 then

begin

serve:=-1;

exit;

end;

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

while true do

begin

if not msgrcv(r_qid, @r_entry, MAXOBN, -1*MAXPRIOR, MSG_NOERROR) then

begin

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

serve:=-1;

exit;

end

else

begin

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

proc_obj (@r_entry);

end;

end;

end;

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

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

Соседние файлы в папке Полищук, Семериков. Системное программирование в UNIX средствами Free Pascal