Межпроцессный обмен
Организация каналов в ОC Windows
Анонимные каналы
Анонимные каналы в Windows - это полудуплексное средство потоковой передачи байтов между родственными процессами. Они функционируют в пределах локальной вычислительной системы и хорошо подходят для перенаправления выходного потока одной программы на вход другой. Анонимные каналы реализованы при помощи именованных каналов с уникальными именами.
Анонимные каналы создаются процессом сервером при помощи функции CreatePipe:
BOOL CreatePipe( PHANDLE hReadPipe, // описатель для чтения PHANDLE hWritePipe, // описатель для записи LPSECURITY_ATTRIBUTES lpPipeAttributes, // атрибуты безопасности DWORD nSize // размер канала );
Функция CreatePipe возвращает два описателя (дескриптора) для чтения и записи в канал. После создания канала необходимо передать клиентскому процессу эти дескрипторы (или один из них), что обычно делается с помощью механизма наследования.
Для наследования описателя нужно, чтобы дочерний процесс создавался функцией CreateProcess с флагом наследования TRUE. Предварительно нужно создать наследуемые описатели. Это можно, например, сделать путем явной спецификации параметра bInheritHandle структуры SECURITY_ATTRIBUTES при создании канала.
Другим способом является создание наследуемого дубликата имеющегося описателя при помощи функции DuplicateHandle и последующая передача его создаваемому процессу через командную строку или каким-либо иным образом.
Получив нужный описатель, клиентский процесс, так же как и сервер, может далее взаимодействовать с каналом при помощи функций ReadFile и WriteFile. По окончании работы с каналом оба процесса должны закрыть описатели при помощи функции CloseHandle.
Прогон программы общения процесса через анонимный канал с самим собой
#include <windows.h> #include <stdio.h> int main() { HANDLE hRead, hWrite; char BufIn[100], *BufOut = "0123456789"; int BufSize = 100; int BytesOut = 10, BytesIn = 5, i; if(!CreatePipe(&hRead, &hWrite, NULL, BufSize)) printf("Create pipe failed.\n"); WriteFile(hWrite, BufOut, BytesOut, &BytesOut, NULL); printf("Write into pipe %d bytes : ", BytesOut); for(i=0; i<BytesOut;i++) printf("%c",BufOut[i]); printf("\n"); ReadFile(hRead, BufIn, BytesIn, &BytesIn, NULL); printf("Read from pipe %d bytes : ", BytesIn); for(i=0; i<5;i++) printf("%c",BufIn[i]); return 0; }
В приведенной программе создается анонимный канал, в него записывается строка цифр, затем часть этой строки читается и выводится на экран.
Прогон программы общения через анонимный канал клиента и сервера
В качестве самостоятельного упражнения рекомендуется организовать через анонимный канал межпроцессное взаимодействие. Процесс-сервер должен создать канал, записать в него информацию и запустить процесс-клиент, задача которого - записанную информацию из канала извлечь.
Именованные каналы
Именованные каналы являются объектами ядра ОС Windows, позволяющими организовать межпроцессный обмен не только в изолированной вычислительной системе, но и в локальной сети. Они обеспечивают дуплексную связь и позволяют использовать как потоковую модель, так и модель, ориентированную на сообщения. Обмен данными может быть синхронным и асинхронным.
Каналы должны иметь уникальные в рамках сети имена в соответствии с правилами именования ресурсов в сетях Windows (Universal Naming Convention, UNC), например, \\ServerName\pipe\PipeName. Для общения внутри одного компьютера имя записывается в форме \\.\pipe\PipeName, где "." обозначает локальную машину. Слово "pipe" в составе имени фиксировано, а PipeName - имя, задаваемое пользователем. Эти имена, подобно именам открытых файлов, не являются именами объектов. Они относятся к пространству имен под управлением драйверов файловых систем именованных каналов ( \Winnt\System32\Drivers\Npfs.sys ), привязанному к специальному объекту устройству \Device\NamedPipe, на которое есть ссылка в каталоге глобальных имен объектов \??\Pipe (эти последние имена "видит" утилита WinObj).
Имена созданных именованных каналов можно перечислить с помощью свободно распространяемой утилиты pipelist с сайта http://www.sysinternals.com. Поскольку имена каналов интегрированы в общую структуру имен объектов, приложения могут открывать именованные каналы с помощью функции CreateFile и взаимодействовать с ними через функции ReadFile и WriteFile.
Использование именованных каналов
Сервер создает именованный канал при помощи функции CreateNamedPipe (см. MSDN).
Помимо имени канала в форме, описанной выше, в число параметров функции входят: флаг, указывающий модель передачи данных; параметр, определяющий синхронный или асинхронный режим работы канала, а также указывающий, должен ли канал быть односторонним или двухсторонним. Кроме того, имеется необязательный дескриптор защиты, запрещающий несанкционированный доступ к именованному каналу, и параметр, определяющий максимальное число одновременных соединений по данному каналу.
Повторно вызывая CreateNamedPipe, можно создавать дополнительные экземпляры этого же канала.
После вызова CreateNamedPipe сервер выполняет вызов ConnectNamedPipe и ждет отклика от клиентов, которые соединяются с каналом при помощи функции CreateFile или CallNamedPipe, указывая при вызове имя созданного сервером канала. Легальный клиент получает описатель, представляющий клиентскую сторону именованного канала, и работа серверной функции ConnectNamedPipe на этом завершается.
После того как соединение по именованному каналу установлено, клиент и сервер могут использовать его для чтения и записи данных через Win32-функции ReadFile и WriteFile.
Прогон программы общения двух процессов через именованный канал
В качестве упражнения рекомендуется осуществить прогон программы общения клиента и сервера через именованный канал.
Сервер
#include <stdio.h> #include <windows.h> void main() { PROCESS_INFORMATION piProcInfo; STARTUPINFO SI; char * ClientName = "client.exe"; HANDLE hPipe; LPTSTR PipeName = TEXT("\\\\.\\pipe\\MyPipe"); char Buff[255]; DWORD iNumBytesToRead = 255, i; ZeroMemory(&SI, sizeof(STARTUPINFO)); SI.cb = sizeof(STARTUPINFO); ZeroMemory(&piProcInfo, sizeof(piProcInfo)); hPipe = CreateNamedPipe( PipeName, // имя канала PIPE_ACCESS_DUPLEX, // чтение и запись из канала PIPE_TYPE_MESSAGE | // передача сообщений по каналу PIPE_READMODE_MESSAGE | // режим чтения сообщений PIPE_WAIT, // синхронная передача сообщений PIPE_UNLIMITED_INSTANCES, // число экземпляров канала 4096, // размер выходного буфера 4096, // размер входного буфера NMPWAIT_USE_DEFAULT_WAIT, // тайм-аут клиента NULL); // защита по умолчанию if (hPipe == INVALID_HANDLE_VALUE) { printf("CreatePipe failed: error code %d\n", (int)GetLastError()); return; } if((CreateProcess(NULL, ClientName, NULL, NULL, FALSE, 0, NULL, NULL, &SI, &piProcInfo))==0) { printf("create client process: error code %d\n", (int)GetLastError()); return; } if((ConnectNamedPipe(hPipe, NULL))==0) { printf("client could not connect\n"); return; } ReadFile(hPipe, Buff, iNumBytesToRead, &iNumBytesToRead, NULL); for(i=0; i<iNumBytesToRead; i++) printf("%c",Buff[i]); }7.1.
Клиент
#include <stdio.h> #include <windows.h> void main() { HANDLE hPipe; LPTSTR PipeName = TEXT("\\\\.\\pipe\\MyPipe"); DWORD NumBytesToWrite; char Buff[] = "Message from Client"; hPipe = CreateFile( PipeName, // имя канала GENERIC_READ | // чтение и запись в канал GENERIC_WRITE, 0, // нет разделяемых операций NULL, // защита по умолчанию OPEN_EXISTING, // открытие существующего канала 0, // атрибуты по умолчанию NULL); // нет дополнительных атрибутов WriteFile(hPipe, Buff, strlen(Buff), &NumBytesToWrite, NULL); }
В данном примере сервер создает канал, затем запускает процесс-клиент и ждет соединения. Далее он читает сообщение, посланное клиентом.
Помимо перечисленных выше система представляет еще ряд полезных функций для работы с именованными каналами. Для копирования данных из именованного канала без удаления их из канала используется функция PeekNamedPipe. Функция TransactNamedPipe применяется для объединения операций чтения и записи в канал в одну операцию, которая называется транзакцией. Имеются информационные функции для определения состояния канала, например, GetNamedPipeHandleState или GetNamedPipeInfo. Полный перечень находится в MSDN.
Заключение
Организация совместной деятельности и общения процессов является важной и актуальной задачей. К основным способам межпроцессного обмена традиционно относят каналы и разделяемую память, для организации которых используют разделяемые ресурсы. Анонимные каналы поддерживают потоковую модель, в рамках которой данные представляют собой неструктурированную последовательность байтов. Именованные каналы, поддерживающие как потоковую модель, так и модель, ориентированную на сообщения, обеспечивают обмен данными не только в изолированной вычислительной среде, но и в локальной сети.