НОВОЕ: OS/2 GURU - Вопросы и ответы

Reviews / articles about OS/2

Operating systems:
ArcaOS, eComStation, IBM OS/2 Warp
Мифы о eComStation 

Unsorted

 

 

ArcaOS 5.0 Русская версия
Пакет русификации ArcaOS 5.0 OS/2 давно доступен. Поддерживается любая версия: 5.0, 5.0.1, 5.0.2.

eCo Software может выпустить и другие пакеты (Немецкий, Голландский, Бразильский Португальский, Испанский, Шведский и т.д.)

Взаимодействие между процессами


TITLE: Взаимодействие между процессами

DATE: 2012-08-11 17:56:09

AUTHOR: Capricorn

Введение

Любой программист, который пишет софт для многозадачной OS (а в наши дни других фактически не существует) рано или поздно сталкивается с необходимостью обеспечить взаимодействие между несколькими программами, которые должны работать в связке. В OS/2 для этой цели существует несколько механизмов: семафоры, пайпы, общая (разделяемая) память, очереди. Если с первыми двумя всё более-менее понятно и программисты активно используют их, то очереди и общая память почему-то обделены вниманием. На мой взгляд, совершенно незаслуженно. Цель данной статьи - подробно рассмотреть данные механизмы и дать рекомендации по их использованию. Но сначала, рассмотрим первые два.

Семафоры

Семафоры служат одной цели: синхронизации процессов. У семафора всегда есть 2 состояния: "занят" и "свободен". Суть синхронизации состоит в том, что соответствующая команда ожидания проверяет, занят ли семафор, и если да, замораживает Всего существует 2 вида семафоров: ивэнты и мутексы.

а) Семафор типа "event" ("событие") нужен для того, чтобы позволить одному треду дождаться, пока другой тред выполнит некоторую задачу.

Пример:

/*************/
/*           */
/* Процесс 1 */
/*           */
/*************/

#define INCL_DOS
#include 

PSZ  szSemName  = "\\SEM32\\EVENT"; /* Имя семафора. Должно иметь префикс \SEM32\ */
HEV  hevEvent1  = 0;                /* Дескриптор семафора */

if (DosCreateEventSem(szSemName,    /* Имя создаваемого семафора  */
                    &hevEvent1,     /* Сюда будет возвращён дескриптор семафора */
                    DC_SEM_SHARED,  /* Разделяемый семафор */
                    FALSE))         /* Создаём семафор в сброшенном (занятом) состоянии */
   return 1;  /* Семафор создать не удалось */

/* Тут что-то делаем 
   До этого момента другой тред будет ждать
   срабатывания (освобождения) семафора */

DosPostEventSem(hevEvent); /* Командуем семафору сработать (освободиться) */
  
/* Продолжаем работу */

DosCloseEventSem(hevEvent1);      /* Избавляемся от семафора */

 

/*************/
/*           */
/* Процесс 2 */
/*           */
/*************/


Трубы (pipes)

[дополнение]

Разделяемая память

[дополнение]

Очереди

Ещё один способ взаимодействия между процессами - очереди (queues). Как уже говорилось ранее, очереди почему-то обойдены вниманием в пользу труб (pipes). На мой взгляд, совершенно незаслуженно. Очень часто очереди оказываются намного удобнее. Но, всё по-порядку.

Итак, очередь представляет собой механизм обмена сообщениями. Они чем-то напоминают очереди сообщений Presentation Manager'а, но есть существенные различия, поэтому не стоит их путать. Главное различие состоит в том, что не смотря на название, очереди могут работать не только в режиме собственно, очередей (FIFO), но и в режиме стека (LIFO), а также, по приоритетам.

Очереди всегда именованные.

Чаще всего, один процесс (тред), который мы условно назовём сервером, создаёт очередь и слушает его, другие (назовём их клиентами), открывают её и пишут туда сообщения. Разумеется, для прослушивани очереди логично организовать цикл.

Пример:

/**********************/
/*                    */
/* Процесс 1 (сервер) */
/*                    */
/**********************/

#define INCL_DOS
#include 

#define QUEUE_FIFO 0	// Очередь
#define QUEUE_LIFO 1	// Стек
#define QUEUE_PRIO 2	// По приоритетам

#define MSG_QUIT   0x0001 /* Идентификатор сообщения для завершения цикла */

#define QUEUE_NAME "\\QUEUES\\special.que"	/* Имя очереди.
						 * Должно иметь префикс \QUEUES\
						 */

/* Функция, которая будет обрабатывать сообщения */
BOOL processMessage(ULONG ulMessageId, ULONG ulDataSize, PVOID pvData);

int main(void)
{
	HQUEUE hQueue;	/* Сюда будет помещён хэндл очереди */
	APIRET rc;	/* Результат вызова функций API */	

	/* Открываем очередь */
	rc = DosCreateQueue(&hQueue, QUEUE_FIFO, QUE_NAME);
	if (rc)
		return rc;
		
	/* Цикл обработки сообщений из очереди */	
	while(TRUE)
	{
		REQUESTDATA requestData;        /* Структура для помещения данных запроса */
		ULONG       ulDataSize;         /* Сюда будет помещён объём присланных данных */
		PVOID       pvData;             /* Сюда будет помещена ссылка на присланные данные */
		rc = DosReadQueue(hQueue,       /* Хэндл очереди */
                                 &requestData, /* Данные запроса */
                                 &ulDataSize,  /* Объём присланных данных */
                                 &pvData,      /* Присланные данные */
                                 0,            /* Читаем первый элемент из очереди */
                                 FALSE,        /* Ждём */
                                 0,            /* Приоритет  не нужен */
                                 NULLHANDLE);  /* Нэндл семафора-собатия  не нужен */
		if (rc || requestData.ulData==MSG_QUIT)
			break;
		processMessage(ulMessageId, ulDataSize, pvData);
	}
	rc = DosCloseQueue(hQueue);             /* Удаляем очередь */
	return rc;
}

 

/**********************/
/*                    */
/* Процесс 2 (клиент) */
/*                    */
/**********************/

#define MSG_QUIT   0x0001 /* Идентификатор сообщения для завершения цикла */

#define QUEUE_NAME "\\QUEUES\\special.que"	/* Имя очереди.
						 * Должно иметь префикс \QUEUES\
#define INCL_DOS
#include 

int main(void)
{
	HQUEUE hQueue;	/* Сюда будет помещён хэндл очереди */
	APIRET rc;	/* Результат вызова функций API */	
	PID	pid;	/* Идентификатор процесса, создавшего очередь */

	/* Открываем очередь */
	rc = DosOpenQueue(&pid, &hQueue, QUE_NAME);
	if (rc)
		return rc;

	rc = DosWriteQueue(hQueue, MSG_QUIT, 0L, NULL, 0L); /* Записываем в очередь сообщение*/
	rc = DosCloseQueue(hQueue);                         /* Закрываем очередь */
	return rc;
}

В данном примере мы первый процесс создаёт и слушает очередь. Другой процесс открывает очередь и записывает туда определённое нами сообщение о завершении работы (MSG_QUIT). Первый процесс, получив это сообщение, завершает свою работу.

Обратите внимание, что функция DosCloseQueue() по сути, удаляет хэндл очереди. Как только все хэндлы очереди будут удалены, удалится и сама очередь.

Так в чём же преимущество очередей перед трубами? Дело в том, что при использовании труб мы фактически имеем дело с непрерывным потоком данных. Это удобно, когда необходимо постоянно передавать большие объёмы данных. Но такие случаи очень редки. Чаще бывает нужно, чтобы один процесс сообщал другому о неких событиях или передавал какие-то команды, на которые другой процесс должен реагировать. В данном случае очереди гораздо удобнее, т. к. сообщение, помещённое в очередь как раз и является такой командой.

 

Попробуй программу:

Как создать зашифрованный диск? NDCrypt - плагин для NetDrive.

Комментарии:

eComStation 2.0 работает быстрее (поддержка SMP x файловая система JFS) Что нового в eCS 2.0?

 


 

(C) OS2.GURU 2001-2021