Обмен данными между параллельными процессами
150.62K
Категория: ПрограммированиеПрограммирование

Обмен данными между параллельными процессами. Работа с анонимными каналами

1. Обмен данными между параллельными процессами

2.

3.

• При обмене данными между параллельными
процессами различают два способа передачи данных:
потоком;
сообщением.
• Если
данные
передаются
непрерывной
последовательностью байтов, то такая пересылка
данных называется передача данных потоком. В
этом случае общая память М, доступная потокам
ядра операционной системы, может и отсутствовать,
а пересылка данных выполняется одним потоком
ядра непосредственно из буфера В1 в буфер B2.
• Если данные пересылаются группами байтов, то
такая группа байтов называется сообщением, а сама
пересылка данных называется передачей данных
сообщениями.

4.

• С точки зрения направления передачи данных
различают следующие виды связей:
полудуплексная связь, т. е. данные по этой связи
могут передаваться только в одном направлении;
дуплексная связь, т. е. данные по этой связи
могут передаваться в обоих направлениях.

5.

• Предполагая, что рассматриваются только
полудуплексные связи, определим возможные
топологии связей. Под топологией связи будем
понимать
конфигурацию
связей
между
процессами-отправителями и адресатами. С
точки зрения топологии различают следующие
виды связей:
1—>1
— между собой связаны только два
процесса;
1—>N — один процесс связан с N процессами;
N—>1 — каждый из N процессов связан с
одним процессом;
N—>М — каждый из N процессов связан с
каждым из М процессов.

6.

7.

Передача сообщений
• Концептуально
обмен
сообщениями
между
процессами выполняется при помощи двух функций:
send — послать сообщение;
receive — получить сообщение.
• Само сообщение состоит из двух частей: заголовка и
тела сообщения. В заголовке сообщения находится
такая служебная информация, как:
тип сообщения;
имя адресата сообщения;
имя отправителя сообщения;
длина сообщения (контрольная информация).
• Тело сообщения содержит само сообщение.

8.

Синхронный и асинхронный обмен
данными
• При передаче данных различают синхронный и
асинхронный обмен данными. Если потокотправитель, отправив сообщение функцией
send,
блокируется
до
получения
этого
сообщения
потоком-адресатом,
то
такое
отправление сообщения называется синхронным.
В противном случае отправление сообщения
называется асинхронным. Если поток-адресат,
вызвавший функцию receive, блокируется до тех
пор, пока не получит сообщение, то такое
получение сообщения называется синхронным. В
противном
случае
получение
сообщения
называется асинхронным.

9.

Буферизация
• Буфером называется вместимость связи между
процессами, т. е. количество сообщений, которые
могут одновременно пересылаться по этой связи.
Различаются три типа буферизации:
нулевая вместимость связи (нет буфера) — в этом
случае возможен только синхронный обмен данными
между процессами;
ограниченная вместимость связи (ограниченный
буфер) — в этом случае, если буфер полон, то
отправитель сообщения должен ждать очистки
буфера хотя бы от одного сообщения;
неограниченная вместимость связи (неограниченный
буфер) — в этом случае отправитель никогда не ждет
при отправке сообщения.

10.

Работа с анонимными каналами в Windows
• Анонимным каналом называется объект ядра
операционной системы, который обеспечивает
передачу
данных
между
процессами,
выполняющимися
на
одном
компьютере.
Процесс, который создает анонимный канал,
называется
сервером
анонимного
канала.
Процессы, которые связываются с анонимным
каналом, называются клиентами анонимного
канала. Другими словами можно сказать, что
анонимный — это такой канал передачи данных
между процессами, который не имеет имени.
Следовательно, доступ к такому каналу имеют
только родительский процесс-сервер и дочерние
процессы-клиенты этого канала.

11.

• Характеристики анонимных каналов, которые
необходимо учитывать при их использовании для
обмена
данными
между
параллельными
процессами:
не имеют имени;
полудуплексные;
передача данных потоком;
синхронный обмен данными;
возможность моделирования любой топологии
связей.

12.

• Анонимные каналы создаются процессомсервером при помощи функции createPipe,
которая имеет следующий прототип:
BOOL CreatePipe(
PHANDLE hReadHandle,
// дескриптор
для чтения из канала
PHANDLE hWriteHandle,
// дескриптор
для записи в канал
LPS ECURITY_ATTRIBUTES lpPipeAttributes,
// атрибуты защиты
DWORD dwSize
// размер буфера в байтах

13.

• Для записи данных в анонимный канал используется
функция WriteFile, которая имеет следующий
прототип:
BOOL WriteFile(
HANDLE hAnonymousPipe,
//дескриптор анонимного канала
LPCVOID lpBuffer,
//буфер данных
DWORD dwNumberOfBytesToWrite,
//количество байтов для записи
LPDWORD lpNumberOfBytesWritten,
//количество записанных байтов
LPOVERLAPPED lpOverlapped
//асинхронный ввод
);

14.

• Для чтения данных из анонимного канала
используется функция ReadFile, которая имеет
следующий прототип:
BOOL ReadFile(
HANDLE hAnonymousPipe,
//дескриптор анонимного канала LPCVOID
lpBuffer,
//буфер данных
DWORD dwNumberOfBytesToRead,
//количество байт для записи
LPDWORD lpNumberOfBytesRead,
//количество
записанных
байтов
LPOVERLAPPED lpOverlapped
//асинхронный ввод
);

15.

BOOL DuplicateHandle(
HANDLE hSourceProcessHandle,
// дескриптор процесса - источника
HANDLE hSourceHandle,
// дубликат дескриптора
HANDLE hTargetProcessHandle,
// дескриптор целевого процесса
LPHANDLE lpTargetHandle,
// дубликат дескриптора
DWORD dwDesiredAccess,
// запрос доступа
BOOL bInheritHandle,
// параметр наследования дескриптора
DWORD dwOptions
// необязательные действия );

16.

#include <windows.h>
#include <conio.h>
int main(int argc, char *argv[])
{
HANDLE hWritePipe;
// преобразуем символьное представление дескриптора в
число
hWritePipe = (HANDLE)atoi(argv[1]);
// ждем команды о начале записи в анонимный канал
_cputs("Press any key to start communication.\n");
_getch();
// пишем в анонимный канал
for (int i = 0; i < 10; i++)
{
DWORD dwBytesWritten;
if (!WriteFile(hWritePipe,&i,sizeof(i),&dwBytesWritten,NULL))

17.

{
_cputs("Write to file failed.\n");
_cputs("Press any key to finish.\n");
_getch();
return GetLastError();
}
_cprintf("The number %d is written to the pipe.\n", i);
Sleep(500);
}
// закрываем дескриптор канала
CloseHandle(hWritePipe);
_cputs("The process finished writing to the pipe.\n");
_cputs("Press any key to exit.\n");
_getch();
return 0;
}

18.

#include <windows.h>
#include <conio.h>
int main()
{
char lpszComLine[80]; // для командной строки
STARTUPINFO si;
PROCESS_INFORMATION pi;
HANDLE hWritePipe, hReadPipe, hInheritWritePipe;
// создаем анонимный канал
if(!CreatePipe(
&hReadPipe, // дескриптор для чтения
&hWritePipe, // дескриптор для записи
NULL, // атрибуты защиты по умолчанию,
// в этом случае дескрипторы
// hReadPipe и hWritePipe ненаследуемые
0)) // размер буфера по умолчанию

19.

{
_cputs("Create pipe failed.\n");
_cputs("Press any key to finish.\n");
_getch();
return GetLastError();
}
// делаем наследуемый дубликат дескриптора
hWritePipe
if(!DuplicateHandle(
GetCurrentProcess(), // дескриптор текущего процесса
hWritePipe, // исходный дескриптор канала
GetCurrentProcess(), // дескриптор текущего процесса
&hInheritWritePipe, // новый дескриптор канала
0, // этот параметр игнорируется
TRUE, // новый дескриптор наследуемый
DUPLICATE_SAME_ACCESS ))// доступ не изменяем

20.

{
_cputs("Duplicate handle failed.\n");
_cputs("Press any key to finish.\n");
_getch();
return GetLastError();
}
// закрываем ненужный дескриптор
CloseHandle(hWritePipe);
// устанавливаем атрибуты нового процесса
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
// формируем командную строку
wsprintf(lpszComLine, "C:\\Client.exe %d",
(int)hInheritWritePipe);
// запускаем новый консольный процесс
if (!CreateProcess(
NULL, // имя процесса

21.

lpszComLine, // командная строка
NULL, // атрибуты защиты процесса по умолчанию
NULL, // атрибуты защиты первичного потока по
умолчанию
TRUE, // наследуемые дескрипторы текущего процесса
// наследуются новым процессом
CREATE_NEW_CONSOLE, // новая консоль
NULL, // используем среду окружения процесса предка
NULL, // текущий диск и каталог, как и в процессе предке
&si, // вид главного окна - по умолчанию
&pi // здесь будут дескрипторы и идентификаторы
// нового процесса и его первичного потока
))
{
_cputs("Create process failed.\n");
_cputs("Press any key to finish.\n");
_getch();

22.

return GetLastError();
}
// закрываем дескрипторы нового процесса
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
// закрываем ненужный дескриптор канала
CloseHandle(hInheritWritePipe);
// читаем из анонимного канала
for (int i = 0; i < 10; i++)
{
int nData;
DWORD dwBytesRead;
if (!ReadFile(
hReadPipe,
&nData,
sizeof(nData),
&dwBytesRead,
NULL))

23.

{
_cputs("Read from the pipe failed.\n");
_cputs("Press any key to finish.\n");
_getch();
return GetLastError();
}
_cprintf("The number %d is read from the pipe.\n",
nData);
}
// закрываем дескриптор канала
CloseHandle(hReadPipe);
_cputs("The process finished reading from the pipe.\n");
_cputs("Press any key to exit.\n");
_getch();
return 0;
}

24.

#include <windows.h>
#include <conio.h>
int main(int argc, char *argv[])
{
HANDLE hWritePipe, hReadPipe;
HANDLE hEnableRead; // для синхронизации обмена
данными
char lpszEnableRead[] = "EnableRead";
// открываем событие, разрешающее чтение
hEnableRead = OpenEvent(EVENT_ALL_ACCESS,
FALSE, lpszEnableRead);
// преобразуем символьное представление
дескрипторов в число
hWritePipe = (HANDLE)atoi(argv[1]);
hReadPipe = (HANDLE)atoi(argv[2]);
// ждем команды о начале записи в анонимный канал

25.

_cputs("Press any key to start communication.\n");
_getch();
// пишем в анонимный канал
for (int i = 0; i < 10; i++)
{
DWORD dwBytesWritten;
if (!WriteFile( hWritePipe, &i, sizeof(i), &dwBytesWritten,
NULL))
{
_cputs("Write to file failed.\n");
_cputs("Press any key to finish.\n");
_getch();
return GetLastError();
}
_cprintf("The number %d is written to the pipe.\n", i);
}
_cputs("The process finished writing to the pipe.\n");

26.

// ждем разрешения на чтение
WaitForSingleObject(hEnableRead, INFINITE);
// читаем ответ из анонимного канала
for (int j = 0; j < 10; j++)
{
int nData;
DWORD dwBytesRead;
if (!ReadFile( hReadPipe, &nData, sizeof(nData),
&dwBytesRead, NULL))
{
_cputs("Read from the pipe failed.\n");
_cputs("Press any key to finish.\n");
_getch();
return GetLastError();

27.

}
_cprintf("The number %d is read from the pipe.\n",
nData);
}
_cputs("The process finished reading from the
pipe.\n");
_cputs("Press any key to exit.\n");
_getch();
// закрываем дескрипторы канала
CloseHandle(hWritePipe);
CloseHandle(hReadPipe);
CloseHandle(hEnableRead);
return 0;
}

28.

#include <windows.h>
#include <conio.h>
int main()
{
char lpszComLine[80]; // для командной строки
HANDLE hEnableRead; // для синхронизации обмена данными
char lpszEnableRead[] = "EnableRead";
STARTUPINFO si;
PROCESS_INFORMATION pi;
HANDLE hWritePipe, hReadPipe;
SECURITY_ATTRIBUTES sa;
// создаем событие для синхронизации обмена данными
hEnableRead = CreateEvent(NULL, FALSE, FALSE,
lpszEnableRead);
// устанавливает атрибуты защиты канала
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL; // защита по умолчанию
sa.bInheritHandle = TRUE; // дескрипторы наследуемые

29.

// создаем анонимный канал
if(!CreatePipe(
&hReadPipe, // дескриптор для чтения
&hWritePipe, // дескриптор для записи
&sa, // атрибуты защиты по умолчанию,
// дескрипторы наследуемые
0)) // размер буфера по умолчанию
{
_cputs("Create pipe failed.\n");
_cputs("Press any key to finish.\n");
_getch();
return GetLastError();
}
// устанавливаем атрибуты нового процесса
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
// формируем командеую строку
wsprintf(lpszComLine, "C:\\Client.exe %d %d",
(int)hWritePipe, (int)hReadPipe);

30.

// запускаем новый консольный процесс
if (!CreateProcess(
NULL, // имя процесса
lpszComLine, // командная строка
NULL, // атрибуты защиты процесса по умолчанию
NULL, // атрибуты защиты первичного потока по умолчанию
TRUE, // наследуемые дескрипторы текущего процесса наследуются
новым процессом
CREATE_NEW_CONSOLE, // новая консоль
NULL, // используем среду окружения процесса предка
NULL, // текущий диск и каталог, как и в процессе предке
&si, // вид главного окна - по умолчанию
&pi // здесь будут дескрипторы и идентификаторы нового процесса и
его первичного потока
))
{
_cputs("Create process failed.\n");
_cputs("Press any key to finish.\n");
_getch();
return GetLastError();
}

31.

// закрываем дескрипторы нового процесса
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
// читаем из анонимного канала
for (int i = 0; i < 10; i++)
{
int nData;
DWORD dwBytesRead;
if (!ReadFile( hReadPipe, &nData, sizeof(nData),
&dwBytesRead, NULL))
{
_cputs("Read from the pipe failed.\n");
_cputs("Press any key to finish.\n");
_getch();
return GetLastError();
}
_cprintf("The number %d is read from the pipe.\n", nData);
}

32.

_cputs("The process finished reading from the pipe.\n");
// даем сигнал на разрешение чтения клиентом
SetEvent(hEnableRead);
// пишем ответ в анонимный канал
for (int j = 10; j < 20; j++)
{
DWORD dwBytesWritten;
WriteFile(hWritePipe, &j, sizeof(j), &dwBytesWritten, NULL);
_cprintf("The number %d is written to the pipe.\n", j);
}
// закрываем дескрипторы канала
CloseHandle(hReadPipe); CloseHandle(hWritePipe);
CloseHandle(hEnableRead);
_cputs("The process finished writing to the pipe.\n");
_cputs("Press any key to exit.\n");
_getch();
return 0;
}
English     Русский Правила