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

Содержание

Слайд 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 Анонимным каналом называется объект ядра

Работа с анонимными каналами в Windows
Анонимным каналом называется объект ядра операционной

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

Характеристики анонимных каналов, которые необходимо учитывать при их использовании для обмена

Характеристики анонимных каналов, которые необходимо учитывать при их использовании для обмена

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

Анонимные каналы создаются процессом-сервером при помощи функции createPipe, которая имеет следующий

Анонимные каналы создаются процессом-сервером при помощи функции createPipe, которая имеет следующий

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

Для записи данных в анонимный канал используется функция WriteFile, которая имеет

Для записи данных в анонимный канал используется функция WriteFile, которая имеет

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

Для чтения данных из анонимного канала используется функция ReadFile, которая имеет

Для чтения данных из анонимного канала используется функция ReadFile, которая имеет

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

BOOL DuplicateHandle( HANDLE hSourceProcessHandle, // дескриптор процесса - источника HANDLE hSourceHandle,

BOOL DuplicateHandle(
HANDLE hSourceProcessHandle,
// дескриптор процесса - источника
HANDLE hSourceHandle,


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

#include #include int main(int argc, char *argv[]) { HANDLE hWritePipe; //

#include
#include
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();

{
_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 #include int main() { char lpszComLine[80]; // для командной строки

#include
#include
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

{
_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

{
_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, // атрибуты защиты процесса по умолчанию

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); //

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");

{
_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 #include int main(int argc, char *argv[]) { HANDLE hWritePipe, hReadPipe;

#include
#include
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(); // пишем в анонимный

_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); // читаем ответ из

// ждем разрешения на чтение
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); }

}
_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 #include int main() { char lpszComLine[80]; // для командной строки

#include
#include
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,

// создаем анонимный канал
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, // имя процесса

// запускаем новый консольный процесс
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); // читаем из анонимного

// закрываем дескрипторы нового процесса
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);
}