Готовые отчеты / ОСиС. Лабораторная работа 8
.pdfФедеральное агентство связи ФЕДЕРАЛЬНОЕ ГОСУДАРСТВЕННОЕ БЮДЖЕТНОЕ
ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ ВЫСШЕГО ОБРАЗОВАНИЯ «САНКТ-ПЕТЕРБУРГСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ ТЕЛЕКОММУНИКАЦИЙ ИМ. ПРОФ. М. А. БОНЧ-БРУЕВИЧА» (СПбГУТ)
Факультет инфокоммуникационных сетей и систем Кафедра программной инженерии и вычислительной техники
ЛАБОРАТОРНАЯ РАБОТА №8 по дисциплине «Операционные системы и сети»
на тему «Разработка многопроцессной программы под Windows»
Выполнил: студент 3-го курса дневного отделения группы ИКПИ-85
Коваленко Леонид Александрович Преподаватель:
доцент кафедры ПИиВТ Дагаев Александр Владимирович
Санкт-Петербург 2020
Цель работы Разработать многопроцессную программу с применением механизмов
синхронизации процессов под Windows. Постановка задачи
1.Написать программу на Си для односторонней передачи данных при помощи пайпа и продемонстрировать ее работу.
2.Написать программу на Си для двусторонней передачи данных при помощи пайпов и продемонстрировать ее работу.
Ход работы
Работа выполняется в операционной системе MS Windows 10 Pro. Напишем программу на Си для односторонней передачи данных при
помощи пайпа (табл. 1).
Таблица 1 — Файл main.c (односторонняя передача данных)
#include <stdio.h> #include <string.h> #include <Windows.h>
#define NAMEDPIPE_NAME "\\\\.\\pipe\\spec_pipe" #define BUFSIZE 50
int main() {
char buffer[BUFSIZE];
// Попытка создания именнованого пайпа
HANDLE hPipe = CreateNamedPipe(TEXT(NAMEDPIPE_NAME), PIPE_ACCESS_OUTBOUND, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, sizeof(char) * BUFSIZE, sizeof(char) * BUFSIZE, NMPWAIT_USE_DEFAULT_WAIT, NULL);
DWORD dwRead;
// Если канал был создан и ошибок не произошло if (GetLastError() == 0) {
while (1) {
printf("Ожидаем клиента...\n"); // Ждем очередного подключения
if (ConnectNamedPipe(hPipe, NULL)) {
// До тех пор, пока соединение не оборвалось while (1) {
memset(buffer, '\0', BUFSIZE); // Очистка printf("Введите сообщение, stop или exit: "); fgets(buffer, BUFSIZE, stdin);
if (strncmp(buffer, "stop", sizeof(char) * 4) == 0) { DisconnectNamedPipe(hPipe);
break;
}
if (strncmp(buffer, "exit", sizeof(char) * 4) == 0) { DisconnectNamedPipe(hPipe);
CloseHandle(hPipe); return 0;
}
// Отправляем сообщение
if (!WriteFile(hPipe, buffer, BUFSIZE, NULL, NULL)) { printf("Клиент покинул канал.\n");
break;
2
}
printf("Отправлено: %s", buffer);
}
}
DisconnectNamedPipe(hPipe);
}
return 0;
}
// Если канал уже был создан, то подключаемся к нему if (GetLastError() == 183) {
hPipe = CreateFile(TEXT(NAMEDPIPE_NAME), GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
if (hPipe == NULL || hPipe == INVALID_HANDLE_VALUE) { printf("ERROR %lu\n", GetLastError());
return -1;
}
printf("Получаем сообщения...\n"); while (1) {
memset(buffer, '\0', BUFSIZE); // Очистка // Получаем сообщение
if (!ReadFile(hPipe, buffer, BUFSIZE, &dwRead, NULL)) { return 0;
}
buffer[dwRead] = '\0'; printf("Получено: %s", buffer);
}
return 0;
}
printf("ERROR %lu\n", GetLastError()); return -1;
}
Продемонстрируем работу программы (рис. 1).
Рисунок 1 — Процесс передачи данных левым процессом правому Сначала левый процесс создает пайп и ожидает правого. Правый
подключается. Затем левый процесс пишет сообщения, а правый читает. Если левый процесс пишет stop или exit, то соединение прерывается. Команда stop используется для прерывания соединения с текущим клиентом и ожидания нового. Команда exit — для выхода из программы.
3
Напишем программу на Си для двусторонней передачи данных при помощи пайпов и потоков (табл. 2).
Таблица 2 — Файл main.c (двусторонняя передача данных)
#include <stdio.h> #include <string.h> #include <Windows.h>
#define NAMEDPIPE1_NAME "\\\\.\\pipe\\spec_pipe_one" #define NAMEDPIPE2_NAME "\\\\.\\pipe\\spec_pipe_two" #define BUFSIZE 50
//Функция симуляции ввода Enter в поток ввода stdin void sendEnterToStdin() {
DWORD dw; INPUT_RECORD ir[2];
for (int i = 0; i < 2; i++) {
KEY_EVENT_RECORD *kev = &ir[i].Event.KeyEvent; ir[i].EventType = KEY_EVENT;
kev->bKeyDown = i == 0; kev->dwControlKeyState = 0; kev->wRepeatCount = 1; kev->uChar.UnicodeChar = VK_RETURN; kev->wVirtualKeyCode = VK_RETURN;
kev->wVirtualScanCode = MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC);
}
WriteConsoleInput(GetStdHandle(STD_INPUT_HANDLE), ir, 2, &dw);
}
//Функция потока записи сервера
DWORD WINAPI runWriteServerThread(LPVOID * arg) { HANDLE hPipe = *(HANDLE *)arg;
char buffer[BUFSIZE]; while (1) {
memset(buffer, '\0', BUFSIZE); printf("#> ");
fgets(buffer, BUFSIZE, stdin);
if (strncmp(buffer, "stop", sizeof(char) * 4) == 0) { break;
}
if (strncmp(buffer, "exit", sizeof(char) * 4) == 0) { _exit(0);
}
if (!WriteFile(hPipe, buffer, BUFSIZE, NULL, NULL)) {
if (GetLastError() == 232 || GetLastError() == 233) { printf("Клиент прервал соединение.\n");
}
else {
printf("WriteThread ERROR %lu\n", GetLastError());
}
fflush(stdout); break;
}
printf("Отправлено: %s", buffer); fflush(stdout);
}
return 0;
}
// Функция потока записи клиента
DWORD WINAPI runWriteClientThread(LPVOID * arg) { HANDLE hPipe = *(HANDLE *)arg;
char buffer[BUFSIZE]; while (1) {
4
memset(buffer, '\0', BUFSIZE); printf("#> ");
fgets(buffer, BUFSIZE, stdin); if (*buffer != '\n') {
if (strncmp(buffer, "exit", sizeof(char) * 4) == 0) { _exit(0);
}
if (!WriteFile(hPipe, buffer, BUFSIZE, NULL, NULL)) {
if (GetLastError() == 232 || GetLastError() == 233) { printf("Сервер прервал соединение.\n");
}
else {
printf("WriteThread ERROR %lu\n", GetLastError());
}
fflush(stdout); return -1;
}
printf("Отправлено: %s", buffer); fflush(stdout);
}
}
}
//Функция потока записи сервера/клиента DWORD WINAPI runReadThread(LPVOID * arg) {
HANDLE hPipe = *(HANDLE *)arg;
DWORD dwRead;
char buffer[BUFSIZE]; while (1) {
memset(buffer, '\0', BUFSIZE);
if (!ReadFile(hPipe, buffer, BUFSIZE, &dwRead, NULL)) { if (GetLastError() == 109 || GetLastError() == 233) {
printf("\nСоединение прервано.\n");
}
else {
printf("\nReadThread ERROR %lu\n", GetLastError());
}
fflush(stdout);
sendEnterToStdin(); return -1;
}
buffer[dwRead] = '\0'; printf("\nПолучено: %s#> ", buffer); fflush(stdout);
}
}
//Функция создания пайпа с записью со стороны сервера
HANDLE createPipeFromServer() {
return CreateNamedPipe(TEXT(NAMEDPIPE1_NAME), PIPE_ACCESS_OUTBOUND, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, sizeof(char) * BUFSIZE, sizeof(char) * BUFSIZE, NMPWAIT_USE_DEFAULT_WAIT, NULL);
}
// Функция создания пайпа с записью со стороны клиента HANDLE createPipeFromClient() {
return CreateNamedPipe(TEXT(NAMEDPIPE2_NAME), PIPE_ACCESS_INBOUND, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, sizeof(char) * BUFSIZE, sizeof(char) * BUFSIZE, NMPWAIT_USE_DEFAULT_WAIT, NULL);
}
int main() {
// Попытка создания пайпа с записью со стороны сервера
HANDLE hPipeFromServer = createPipeFromServer(), hPipeFromClient;
5
// Если канал был успешно создан и ошибок не произошло if (GetLastError() == 0) {
while (1) {
//Создаем второй пайп с записью со стороны клиента hPipeFromClient = createPipeFromClient();
if (GetLastError() != 0) {
printf("ERROR %lu\n", GetLastError()); return 1;
}
printf("Ожидаем клиента...\n"); fflush(stdout);
//Ждем очередного подключения
if (ConnectNamedPipe(hPipeFromServer, NULL)) { // До тех пор, пока соединение не оборвалось
printf("Отправьте сообщения / прервите соединение (stop) / выход (exit)\n");
fflush(stdout);
HANDLE hWriteThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) &runWriteServerThread, &hPipeFromServer, 0, NULL);
HANDLE hReadThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) &runReadThread, &hPipeFromClient, 0, NULL);
WaitForSingleObject(hWriteThread, INFINITE); TerminateThread(hReadThread, 0); CloseHandle(hWriteThread); CloseHandle(hReadThread);
FlushFileBuffers(hPipeFromServer);
DisconnectNamedPipe(hPipeFromServer);
DisconnectNamedPipe(hPipeFromClient);
CloseHandle(hPipeFromClient);
}
}
return 0;
}
// Если канал уже был создан, то подключаемся к нему if (GetLastError() == 183) {
hPipeFromServer = CreateFile(TEXT(NAMEDPIPE1_NAME), GENERIC_READ, 0, NULL, OPEN_ALWAYS, 0, NULL);
hPipeFromClient = CreateFile(TEXT(NAMEDPIPE2_NAME), GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, 0, NULL);
if (hPipeFromServer == INVALID_HANDLE_VALUE || hPipeFromClient == INVALID_HANDLE_VALUE) {
if (GetLastError() == 231) { printf("Все пайпы заняты."); return 2;
}
printf("ERROR %lu\n", GetLastError()); return 3;
}
printf("Отправьте сообщения / прервите соединение (exit)\n"); fflush(stdout);
HANDLE hWriteThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) &runWriteClientThread, &hPipeFromClient, 0, NULL);
HANDLE hReadThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) &runReadThread, &hPipeFromServer, 0, NULL);
WaitForSingleObject(hWriteThread, INFINITE); TerminateThread(hReadThread, 0);
return 0;
}
// Если ничего не удалось, печатаем ошибку printf("ERROR %lu\n", GetLastError()); return 4;
}
Продемонстрируем работу программы (рис. 2).
6
Рисунок 2 — Процесс коммуникации двух процессов Сначала левый процесс создает пайпы и ожидает правого. Правый
подключается. Затем оба читают и пишут друг другу сообщения. Если левый процесс пишет stop или exit, то соединение прерывается. Команда stop используется для прерывания соединения с текущим клиентом и ожидания нового. Команда exit — для выхода из программы. При попытке третьего процесса подключиться к занятым пайпам выводится ошибка «Все пайпы заняты.».
Важно понимать, что пайпы (каналы) в Windows, в отличие от Linux, смещены к взаимодействию «клиент-сервер» и работают отчасти подобно сокетам. Но все же клиент-серверное взаимодействие лучше осуществлять при помощи сокетов. Пайпы скорее для конвейерной обработки.
В Windows, в отличие от Linux, имеется понятие двустороннего пайпа. На самом деле понятно, что пайп — это труба с двумя концами: в один момент времени только один может писать, а другой читать. Другое дело — если писатель и читатель меняются местами. Тогда двусторонний пайп — то, что нужно. Но если нужно чтение и запись с обоих сторон без специальной логики по смене мест, то потребуется два пайпа — с записью со стороны сервера и с записью со стороны клиента (как в примере).
Заключение В результате выполнения лабораторной работы мы ознакомились с
применением механизмов синхронизации процессов под Windows.
7