- •Глава 2. Синхронизация задач с использованием api-функций и механизмов ядра.
- •§0. Объекты ядра. Основы.
- •Учет пользователей объектов ядра
- •Процесс и поток: краткая характеристика
- •Создание объекта ядра
- •Закрытие объекта ядра
- •Синхронизация объектов
- •Наследование описателей объекта
- •Именованные объекты
- •3. После этого вызова система проверяет, существует ли уже объект ядра с таким же именем. Если существует, то
- •Open-функции
- •Дублирование описателей объектов
- •1) Процесс-катализатор.
- •2) Процесс-источник.
- •3) Процесс-приемник.
- •Исходное состояние
- •1 0XF0000000 (неприм.) (неприм.)
- •1 0XF0000000 (неприм.) (неприм.)
- •Состояние после вызова DuplicateHandle
- •§1. Синхронизация задач с использованием функций ожидания.
- •Функция ожидания одного объекта
- •Функция ожидания нескольких объектов
- •Функция ожидания нескольких объектов и сообщений
- •Функция создания дочернего процесса
- •Функция завершения дочернего процесса из самого дочернего процесса
- •Функция завершения дочернего процесса из процесса родителя
- •Функция создания вторичного потока
- •Функция завершения дочернего потока из самого дочернего потока (only c)
- •Функция завершения дочернего потока из потока родителя
- •If (my_file.Is_open()) { /// Если удалось открыть
- •Функция создания вторичного потока _beginthread
- •Функция создания вторичного потока _beginthreadex
- •§2. Синхронизация задач с помощью объектов ядра «событие» (Event).
- •Функция создания события
- •Функция установки сигнального состояния события
- •Функция установки несигнального состояния события
- •Функция открытия существующего именованного объекта события
- •Дескриптор защиты (структура)
- •§3. Синхронизация задач с помощью объектов ядра «семафор» (Semaphore).
- •Функция создания семафора
- •Функция открытия семафора
- •Функция увеличения счетчика семафора на указанное количество
- •If (!ReleaseSemaphore( ghSemaphore, 1, null)) /// Если ошибка
- •§4. Синхронизация задач с помощью объектов ядра «мьютекс» (Mutex).
- •Функция создания мьютекса
- •Функция открытия существующего именованного объекта мьютекса
- •Функция освобождения владельца указанного объекта мьютекса
- •§5. Синхронизация задач с помощью объектов ядра «уведомление об изменении» (Change Notification).
- •Функция создания объекта ядра «уведомление об изменении»
- •Функция перезапуска объекта ядра «уведомление об изменении»
- •Функция остановки мониторинга дескриптора объекта ядра «уведомление об изменении»
- •§6. Синхронизация задач с помощью объектов ядра «таймер ожидания» (Waitable Timer).
- •Функция создания объекта ядра «таймер ожидания»
- •Функция активации объекта ядра «ожидаемый таймер»
- •Функция открытия объекта ядра «ожидаемый таймер»
- •1. Функцией CancelWaitableTimer().
- •2. Функцией SetWaitableTimer().
- •If (bSuccess) /// Если успешно, то
- •If (bSuccess) /// Если успешно, то
- •§7. Синхронизация задач с помощью объектов ядра «канал» (Pipe).
- •Анонимный канал
- •Создание анонимных каналов
- •Соединение клиентов с анонимным каналом
- •Обмен данными по анонимному каналу
- •Именованный канал
- •Функция создания именованного канала
- •Функция соединения сервера с клиентом
- •Отключение сервера от клиента
- •Функция ожидания операции именованного канала
- •Функция объединения функций именованного канала
- •Подключение клиента к серверу
Обмен данными по анонимному каналу
Для обмена данными по анонимному каналу в операционных системах Windows используются те же функции, что для записи и чтения данных в файл.
Для записи данных в анонимный канал используется функция WriteFile.
BOOL WriteFile(
HANDLE hAnonPipe,
LPCVOID lpBuffer,
DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten,
LPOVERLAPPED lpOverlapped
);
Параметры и описание:
(1) hAnonPipe — определяет дескриптор анонимного канала.
(2) lpBuffer — определяет буфер данных.
(3) nNumberOfBytesToWrite — определяет число байт для записи.
(4) lpNumberOfBytesWritten — определяет число записанных байт.
(5) lpOverlapped — предназначен для выполнения асинхронной операции вывода.
Возвращаемое значение.
При успешном завершении возвращает значение TRUE, а в случае неудачи – FALSE.
Вызов WriteFile не возвращается, пока он не записал указанное число байтов в канал или не произойдет ошибка.
Описание.
Функция WriteFile записывает в анонимный канал количество байт, заданных параметром nNumberOfBytesToWrite, из буфера данных, на который указывает параметр lpBuffer.
Дескриптор вывода этого анонимного канала должен быть задан первым параметром функции WriteFile.
Количество байт, записанных этой функцией в анонимный канал, возвращается в переменной, на которую указывает параметр lpNumberOfBytesWritten.
Параметр lpOverlapped предназначен для выполнения асинхронной операции вывода, так как анонимные каналы поддерживают только синхронную передачу данных, то в нашем случае этот параметр всегда будет равен NULL.
Для чтения данных из анонимного канала используется функция ReadFile.
BOOL ReadFile(
HANDLE hAnonPipe,
LPCVOID lpBuffer,
DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten,
LPOVERLAPPED lpOverlapped
);
Параметры и описание:
(1) hAnonPipe — определяет дескриптор анонимного канала.
(2) lpBuffer — определяет буфер данных.
(3) nNumberOfBytesToWrite — определяет число байт для записи.
(4) lpNumberOfBytesWritten — определяет число записанных байт.
(5) lpOverlapped — предназначен для выполнения асинхронной операции вывода.
Возвращаемое значение.
При успешном завершении возвращает значение TRUE, а в случае неудачи – FALSE.
Вызов ReadFile возвращается, когда другой канал записал в канал. Вызов ReadFile также может возвращаться, если все дескрипторы записи в канал были закрыты или если произошла ошибка до завершения операции чтения.
Описание.
Функция ReadFile читает из анонимного канала количество байт, заданных параметром nNumberOfBytesToRead, в буфер данных, на который указывает параметр lpBuffer.
Дескриптор ввода этого анонимного канала должен быть задан первым параметром функции ReadFile.
Количество байт, прочитанных функцией WriteFile из анонимного канала, возвращается в переменной, на которую указывает параметр lpNumberOfBytesRead. Также как и в случае записи в анонимный канал параметр lpOverlapped должен быть равен NULL.
Итог
Дескриптор для записи в анонимный канал должен быть параметром функции WriteFile, а дескриптор для чтения из анонимного канала должен быть параметром функции ReadFile. В этом и состоит смысл передачи данных по анонимному каналу только в одном направлении. Однако это не означает, что один процесс может использовать анонимный канал только для записи или только для чтения. Один и тот же процесс может, как писать данные в анонимный канал, так и читать данные из него, должным образом используя дескрипторы этого анонимного канала.
После завершения обмена данными по анонимному каналу, потоки должны закрыть дескрипторы записи и чтения анонимного канала, используя функцию CloseHandle.
Схема обмена данными по анонимному каналу
Процесс-сервер.
1. Создает анонимный канал.
2. Создает дубликат дескриптора, устанавливая должное наследование с помощью DuplicateHandle.
3. Закрывает ненужный дублированный дескриптор.
4. Подготавливает входные параметры для функции CreateProcess.
5. Создает дочерний процесс с помощью функции CreateProcess, задавая bInheritHandle равным TRUE. fdwCreate (dwCreationFlags) устанавливается в CREATE_NEW_CONSOLE (0x00000010).
6. Закрывает дескрипторы нового процесса и потока.
7. Выполняет чтение из анонимного канала.
8. Закрывает дескриптор чтения из канала.
Процесс-клиент.
1. Записывает данные в анонимный канал.
2. Закрывает дескриптор чтения из канала.
Пример (C++).
Запускается только SERVER.EXE
CLIENT
#include <stdio.h>
int main () {
int x, y;
scanf("%d%d",&x,&y); /// Получаем два числа
printf("%d", x+y); /// Складываем два числа
return 0;
}
SERVER
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <iostream>
#define CLIENT_FILE "Client1.exe" /// EXE файл клиента
using namespace std;
int main() {
setlocale(LC_ALL, "Russian"); /// Русский язык
char buf[1024];
SECURITY_ATTRIBUTES sa;
PROCESS_INFORMATION pi;
HANDLE newstdread, newstdwrite; /// Дескрипторы
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = true; /// Наследование
/// Создаем канал
if (!CreatePipe(&newstdread, &newstdwrite, &sa, 0)) {
cout << "Pipe не создан...\n";
getchar();
return 0;
}
else cout << "Pipe создан!\n";
STARTUPINFO si = {sizeof(si)};
ZeroMemory(&si, sizeof(STARTUPINFO)); /// Очистка
/// Далее настройка информации о процессе
si.cb = sizeof(STARTUPINFO);
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.wShowWindow = SW_NORMAL;
si.hStdInput = newstdread; /// Перенаправляем поток ввода
si.hStdOutput = newstdwrite; /// Перенаправляем поток вывода
si.hStdError = si.hStdOutput; /// Перенаправляем поток ошибок
TCHAR czCommandLine[] = CLIENT_FILE; /// Наш клиент
/// Создаем процесс
if (!CreateProcess(NULL, czCommandLine, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) {
cout << "Процесс не создан...\n";
getchar();
CloseHandle(newstdread);
CloseHandle(newstdwrite);
return 0;
}
else
cout << "Процесс создан!\n";
unsigned long with_file;
/// Доп. информация
cout << "STD INPUT HANDLE = " << newstdread << endl;
cout << "STD OUTPUT HANDLE = " << newstdwrite << endl;
strcpy(buf,"332 633\n"); /// Два числа отправляем в поток ввода клиента
WriteFile(newstdwrite, buf, strlen(buf), &with_file, NULL);
Sleep(100); /// Чуть-чуть спим
memset(buf, '\0', sizeof(buf));
ReadFile(newstdread, buf, 100, &with_file, NULL);
printf("Результат: %s\n",buf); /// Получаем результат от клиента
TerminateProcess(pi.hProcess,0); /// Закрываем процесс
CloseHandle(pi.hThread); /// Закрываем дескрипторы
CloseHandle(pi.hProcess); /// Закрываем дескрипторы
CloseHandle(newstdread); /// Закрываем дескрипторы
CloseHandle(newstdwrite); /// Закрываем дескрипторы
getchar(); /// Пауза перед выходом
return 0;
}