- •Лекция 9,10. Примитивы синхронизации вWin32 api
- •Функции ожидания
- •Взаимное исключение (mutex)
- •Использование объекта mutex
- •Семафор
- •Использование семафора
- •Событие
- •Использование событий
- •Ожидаемый таймер
- •Неименованый канал (магистраль) - Anonymous pipe
- •Наследование дескрипторов
- •Именованый канал – Named Pipe
- •Пример: Многонитевой сервер каналов.
Наследование дескрипторов
В параметрах функции CreatePipe в структуре SECURITY_ATTRIBUTES необходимо установить bInheritHandle равным TRUE, тогда дескрипторы, созданные CreatePipe, могут быть унаследованы.
Функция CreateProcess должна разрешить наследование дескрипторов дочерними процессами.
Когда дочерний процесс наследует дескриптор канала он получает возможность обратиться к каналу. Обычно схема передачи дескрипторов дочернему процессу (для случая, когда дочерний процесс пишет в канал) выглядит следующим образом.
Процесс – родитель:
Вызывае функцию GetStdHandle, чтобы получить текущий дескриптор стандартного вывода и сохраняет предыдущее значение.
Вызывает функцию SetStdHandle, чтобы переназначить стандартный вывод на дескриптор записи канала.
Создает дочерний процесс.
Вызывает функцию CloseHandle, чтобы закрыть дескриптор записи к каналу. (После того, как дочерний процесс наследует дескриптор записи, родительский процесс больше не нуждается в копии.)
Вызывает SetStdHandle, чтобы восстановить первоначальный дескриптор стандартного вывода.
Использует функцию ReadFile для получения данных от дочернего процесса.
Дочерний процесс:
Вызывае функцию GetStdHandle, чтобы получить текущий дескриптор стандартного вывода (дескриптор записи в канал).
Использует функцию WriteFile для передачи данных процессу – родителю.
Данные записываются в неименованный канал как поток байтов. Эти означает, что родительский процесс, читающий из канала не может различать сколько отдельных операций записи выполнено в дочернем процессе. Когда все дескрипторы записи к каналу закрыты, ReadFile возвращает нуль. Важно, чтобы родитель закрыл дескриптор записи канала, если это не выполнено, то операция ReadFile не сможет вернуть нуль, потому что родительский процесс имеет открытый дескриптор записи к каналу.
Именованый канал – Named Pipe
Каждый именованный канал имеет уникальное имя, которое отличает его от других именованных каналов в системе. Имя канала задается при вызове функции CreateNamedPipe. Канал может быть создан только на локальном компьютере. Клиент канала указывает имя, когда вызывают функцию CreateFile или CallNamedPipe, чтобы соединиться с существующим именованным каналом.
Имя задается в следующей форме: \\ServerName\pipe\PipeName
Здесь ServerNameявляется именем удаленного компьютера или точкой (локальный компьютер). В имени каналаPipeNameверхний и нижний регистры не различаются.
Пример: Многонитевой сервер каналов.
Основная нить (сервер) в цикле создает канал и ждет подключение клиента к каналу. Когда клиент соединяется, сервер канала создает нить для обслуживания этого клиента и затем продолжает цикл. Клиент может успешно соединиться с каналом в интервале между запросами к функциям CreateNamedPipe и ConnectNamedPipe. Если это случается, ConnectNamedPipe возвращает нуль и GetLastError возвращает ERROR_PIPE_CONNECTED.
Нить, созданная для обслуживания канала, читает запросы и записывает ответы обратно в канал, пока клиент не закроет дескриптор. Когда это случается, нить отсоединяется, закрывает дескриптор канала, и заканчивается.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
VOID InstanceThread(LPVOID);
VOID GetAnswerToRequest(LPTSTR, LPTSTR, LPDWORD);
int xx = 0;
DWORD main(VOID)
{
BOOL fConnected;
DWORD dwThreadId;
HANDLE hPipe, hThread;
LPTSTR lpszPipename = "\\\\.\\pipe\\mynamedpipe";
for (;;)
{
hPipe = CreateNamedPipe(
lpszPipename, // pipe name
PIPE_ACCESS_DUPLEX, // read/write access
PIPE_TYPE_MESSAGE | // message type pipe
PIPE_READMODE_MESSAGE | // message-read mode
PIPE_WAIT, // blocking mode
PIPE_UNLIMITED_INSTANCES, // max. instances
BUFSIZE, // output buffer size
BUFSIZE, // input buffer size
PIPE_TIMEOUT, // client time-out
NULL); // no security attribute
if (hPipe == INVALID_HANDLE_VALUE) MyErrExit("CreatePipe");
fConnected = ConnectNamedPipe(hPipe, NULL) ?
TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
if (fConnected)
{
// Create a thread for this client.
hThread = CreateThread(
NULL, // no security attribute
0, // default stack size
(LPTHREAD_START_ROUTINE) InstanceThread,
(LPVOID) hPipe, // thread parameter
0, // not suspended
&dwThreadId); // returns thread ID
if (hThread == NULL) MyErrExit("CreateThread");
}
else
// The client could not connect, so close the pipe.
CloseHandle(hPipe);
}
return 1;
}
VOID InstanceThread(LPVOID lpvParam)
{
CHAR chRequest[BUFSIZE];
CHAR chReply[BUFSIZE];
DWORD cbBytesRead, cbReplyBytes, cbWritten;
BOOL fSuccess;
HANDLE hPipe;
// The thread's parameter is a handle to a pipe instance.
hPipe = (HANDLE) lpvParam;
while (1)
{
// Read client requests from the pipe.
fSuccess = ReadFile(
hPipe, // handle to pipe
chRequest, // buffer to receive data
BUFSIZE, // size of buffer
&cbBytesRead, // number of bytes read
NULL); // not overlapped I/O
if (! fSuccess || cbBytesRead == 0)
break;
GetAnswerToRequest(chRequest, chReply, &cbReplyBytes);
// Write the reply to the pipe.
fSuccess = WriteFile(
hPipe, // handle to pipe
chReply, // buffer to write from
cbReplyBytes, // number of bytes to write
&cbWritten, // number of bytes written
NULL); // not overlapped I/O
if (! fSuccess || cbReplyBytes != cbWritten) break;
}
FlushFileBuffers(hPipe);
DisconnectNamedPipe(hPipe);
CloseHandle(hPipe);
}
