МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ
НАЦІОНАЛЬНИЙ УНІВЕРСИТЕТ “ЛЬВІВСЬКА ПОЛІТЕХНІКА”
Кафедра ЕОМ
/
КУРСОВА РОБОТА
з курсу
“Системне програмне забезпечення”
На тему:
«Порівняння багатопоточності і багатопроцесності в операційній системі Windows»
2017
Зміст
АНОТАЦІЯ 3
Завдання на курсову роботу 4
Вступ 5
Огляд методів міжпроцесорної комунікації у ОС Windows. 6
Повідомлення WM_COPYDATA 6
Проекція файлу на адресний простір процесу 6
Вибір технологій програмування 8
Розробка алгоритму виконання багатопоточної версії програми. 9
Розробка алгоритму виконання версії програми що використовує дочірній процес. 10
Опис інтерфейсу та інструкції користувача. 11
Тестування 12
Висновок 13
Список використаної літератури 14
Додатки 15
АНОТАЦІЯ
В курсовій роботі була розроблена програма що ілюструє різницю між програмуванням з використанням процесів і програмуванням багатопотокових програм, вона симулює рух кульок у просторі і їхні зіткнення.
Програма була розроблена за допомогою Visual Studio 2017 і працює на базі C++ та специфікації OpenGL з використанням WinApi функцій.
Завдання на курсову роботу
Розробити програму що ілюструє різницю між програмуванням з використанням процесів і програмуванням багатопотокових програм для операційної системи Windows з наступними функціями:
Кількість кульок і потоків задається користувачем
Зіткення кульок абсолютно пружні
- Обчислення траекторії, швидкості і симуляція зіткненнь виконується в окремих потоках і в окремому процесі(для версії що використовує процеси)
Програма повинна бути написана на мові програмування С++ і використовувати WinApi функції.
Вступ
З розвитком комп’ютерної техніки програміст отримує у своє розпорядження все більші обчислювальні потужності а сучасні процесори за останнє десятиліття перейшли від 4 ядерих процесорів до 8 ядерних а сьогодні ми можем отримати навіть 16 ядерні процесори.
Отже сучасні процесори 8 ядерні процесори мають 16 потоків які може використовувати програміст. Як можна зрозуміти, виникає питання де і коли нам необхідне використання потокового розпаралелення, в яких програмах ми отримаємо збільшення продуктивності. Також операційні системи мають в своєму розпорядженні безліч функцій для для роботи з процесами, створення дочірніх процесів, міжпроцесорної комунікації. Тож перед програмістом який хоче щоб його програма виконувалась паралельно постає багато питаннь повязаних засобами.
Велика кількість програм потребує паралельного виконання, це зокрема програми що дозволяють обрацювати відео, фото та 3D-графіку. Такі складні програмні продукти як інтернет-переглядач “Opera” чи текстовий редактор “Word” використовують одночасно і засоби багатопоточного програмування і декомпозицію програми на різні процеси. Програми які не використовують засоби паралельного виконання не тільки не ефективно використовують процесорні ресурси але і можуть «зависати» на комю’терах з низькою продуктивністю на ядро, якщо ми беремо до уваги комерційні програмні продукти то добре розпаралелені програми дозволяють знизити ситемні вимоги і тим самим збільшити число потенційних покупців.
Як бачимо можна виділити цілий клас програмних продуктів що вимагають паралельного виконання на основі процесів і потоків. Тому програмісту необхідно вміти працювати з засобами які дозволяють реалізовувати таке розпаралелення, ці засоби можуть бути частиною мови програмування або це можуть бути засоби операційної системи.
Огляд методів міжпроцесорної комунікації у ОС Windows.
Повідомлення WM_COPYDATA
WM_COPYDATA
wParam = (WPARAM) (HWND) hwnd; - дескриптор передавального вікна
lParam = (LPARAM) (PCOPYDATASTRUCT) pcds; - покажчик на структуру з даними
hwnd
Ідентифікує вікно, яке передає дані.
pcds
Вказує на структуру COPYDATASTRUCT, яка містить дані для передачі.
Якщо приймаюча програма обробляє це повідомлення, вона повинна повернути значення ІСТИНА (TRUE); в іншому випадку вона повинна повернути - БРЕХНЯ (FALSE).
Для передачі цього повідомлення програма повинна використовувати функцію SendMessage, а не функцію PostMessage. Дані, призначені для передачі, не повинні містити покажчиків або інших посилань на об'єкти, недоступні для програми, що приймає ці дані.
До тих пір, поки це повідомлення діє, викликані дані не повинні бути змінені іншим по-струмом процесу пересилання. Приймаюча програма повинна брати до уваги дані тільки для читання. Параметр pcds правильний тільки протягом обробки повідомлення. Приймаюча програма не повинна звільняти пам'ять, викликану pcds. Якщо приймаюча програма звернулася до даних після повернення значення функцією SendMessage, вона має копіювати дані в локальний буфер.
Проекція файлу на адресний простір процесу
Після створення об'єкта "проекція файлу" необхідно, щоб операційна система зарезервувала регіон адресного простору під файл і виділила фізичну пам'ять. Для цього призначена функція MapViewOfFile ():
LPVOID WINAPI MapViewOfFile (
HANDLE hFileMappingObject, дескриптор проекції файлу
DWORD dwDesiredAccess, доступ до даних
DWORD dwFileOffsetHigh, зсув у файлі для відображення
DWORD dwFileOffsetLow, як 64-бітове значення
DWORD dwNumberOfBytesToMap); розмір уявлення Параметр dwDesiredAccess може приймати значення:
file_map_read - файл доступний для читання;
file_map_write - повний доступ для читання і запису;
file_map_copy - дані можна читати, але при записі створюються копії сторінок пам'яті.
32-бітові параметри dwFileOffsetHigh і dwFileOffsetLow визначають старшу і молодшу частину 64-бітного зміщення в файлі. З цього байта і відбувається проектування файлу.
Вибір технологій програмування
Згідно завдання моя програма повинна бути написана на C++ та для операційної системи Windows. Для реалізації багатопоточності я застосую вбудованні засоби мови C++, оголошені в заголовочному файлі <thread>. Для багатопроцесної версії програми я застосую WinAPI-функції і механізм WM_COPYDATA для обміну даними між процесами. Симуляція руху кульок буде побудована на базі специфікації OpenGL.
Оскільки засоби багатопоточності є частиною стандартної бібліотеки C++ вони простіші у використанні ніж WinAPI-функції, також для випадків коли програма повинна бути портована на інші операційні системи, це спрощує процес портування.
WM_COPYDATA є найпростішим з розглянутих мною механізмів зв’язку між процесами. Цей механізм підходить для передані структур даних при умові що структура не містить вказівників.
OpenGl – це специфікація, реалізація якої дозволяє створювати 3D-графіку. Перевагою OpenGl перед іншими спецефікаціями, такими як DirectX є відносна простота та мультиплатформеність.
Розробка алгоритму виконання багатопоточної версії програми.
Граф схеми алгоритму програми.
Рис. 7 Граф-схема алгоритму
Блок 1 – Початок роботи
Блок 2 – Отримання кілкості кульок.
Блок 3 – Отримання кількості процесів.
Блок 4 – Виконання функції moving в визначеній кількості потоків.
Блок 5 – Виконання фукнкції draw головним потоком.
Блок 6 – Очікування натискання користувачем клавіші esc.
Блок 7 – Завершення роботи.
Розробка алгоритму виконання версії програми що використовує дочірній процес.
Граф схеми алгоритму програми.
Рис. 7 Граф-схема алгоритму
Блок 1 – Початок роботи
Блок 2 – Отримання кілкості кульок.
Блок 3 – Створення дочірного процесу.
Блок 4 – Обмін даними з дочірнім процесом.
Блок 5 – Виконання фукнкції draw головним потоком.
Блок 6 – Очікування натискання користувачем клавіші esc.
Блок 7 – Завершення роботи.
Опис інтерфейсу та інструкції користувача.
Оскільки користувач вводить тільки кількість кульок для симуляції(та кількість потоків та версій що використовує багатопоточність) моя програма має консольний інтерфейс. Під час симуляції користувач може переміщувати камеру за допомогую клавіш WASD. Програма завершує виконання після натискання клавіщі ESC
/
Рис.1. Інтерейс користувача
Тестування
Відлагодження програми відбувається за допомогою автоматизованого відлагоджувача який присутній в середовищі Visual Studio 2017, в покроковому режимі перевіряється значення потрібних змінних і вмістиме потрібних структур даних. За допомогою breakpoints відбувається запинка виконання програми в тих місцях де відбулася логічна помилка або в місцях визначених студентом.
Висновок
Підчас створення програмного продукту я навчився навичкам роботи з багатопотоківстю і WinAPI.
На початкових стадіях розробки було проведено аналіз задачі проекту та розроблено граф схеми роботи програмного продукту. Наступні етапи проектування, використовуючи загальнотеоретичні положення сформульовані на ранніх стадіях синтезу, реалізовували проект на програмному рівні.
У курсовій роботі була розроблена програма для наочної ілюстрації можлимостей розпаралелення програм засобами OC Windows. Також було проведене тестування всіх модулів, яке підтвердило, що весь код який міг призвести до помилок опрацьований і дозволяє працювати програмі коректно.
Під час виконання курсової роботи було успішно засвоєно методи розробки системних програм, а зокрема робота з багатопотовістю.
Список використаної літератури
1. Системное программное обеспечение А.В.Гордеев, А.Ю.Молчанов.
2. Дж.Донован. Системное программирование.
3. Литвиненко Н. А. Л. Технология программирования на С++. Win32 API-приложения.
4. Jeff Molofee NeHe. OpenGL Tutorial
Додатки
#include <windows.h>
#include <Winuser.h>
#include <cmath>
#include <gl\glut.h>
#include <vector>
#include <cstdlib>
#include <iostream>
#include <thread>
#include <mutex>
#include <random>
using namespace std;
int fire = 0;
std::mutex mtx;
const int WSCREEN = 1366;
const int HSCREEN = 768;
float shottime = 0;
float lifetime = 2000;
float shot = 0;
struct P
{
float x;
float y;
float z;
float vx;
float vy;
float vz;
float rad;
float life;
float r;
float g;
float b;
};
vector<P> p;
static HGLRC hRC; //Постійний дескриптор рендеренгу
static HDC hDC; //Приватний контекст пристрою GDI
BOOL keys[256];
const float PI = 3.141592653;
const float height = 0;
float x = 0;
float y = 500;
float z = 2500;
float distans = 200;
float angleX = 0;
float angleY = 0;
POINT mousexy;
double Playerspeed = 5;
int xmax = 1000;
int xmin = -1000;
int ymax = 1000;
int ymin = 0;
int zmax = 1000;
int zmin = -1000;
void line(GLfloat x1, GLfloat y1, GLfloat z1, GLfloat r1, GLfloat g1, GLfloat b1, GLfloat x2, GLfloat y2, GLfloat z2, GLfloat r2, GLfloat g2, GLfloat b2)
{
glBegin(GL_LINES);
glColor3ub(r1, g1, b1); glVertex3f(x1, y1, z1);
glColor3ub(r2, g2, b2); glVertex3f(x2, y2, z2);
glEnd();
}
void moving(int &counter)
{
thread_local std::mt19937 generator(std::random_device{}());
std::uniform_real_distribution<double> distribution(1, 255);
if (counter > 0) {
P p0 = {
x + distribution(generator),
y + distribution(generator),
z + distribution(generator),
-20 * sin(distribution(generator) / 180 * PI),
20 * tan(distribution(generator) / 180 * PI),
-20 * cos(distribution(generator) / 180 * PI),
20,
lifetime,
distribution(generator),
distribution(generator),
distribution(generator)
};
std::lock_guard<std::mutex> lock(mtx);
p.push_back(p0);
--counter;
}
std::lock_guard<std::mutex> lock(mtx);
if (GetAsyncKeyState(VK_LBUTTON) && !shot)
{
P p0 = {
x,
y,
z,
-20 * sin(angleX / 180 * PI),
20 * tan(angleY / 180 * PI),
-20 * cos(angleX / 180 * PI),
20,
lifetime,
distribution(generator),
distribution(generator),
distribution(generator)
};
p.push_back(p0);
shot = 1;
shottime = 2;
}
if (shot) shottime--;
if (!shottime) shot = 0;
for (vector<P>::iterator i = p.begin(); i != p.end(); ++i)
{
float x = i->x; float y = i->y; float z = i->z;
float vx = i->vx; float vy = i->vy; float vz = i->vz;
if (x > xmax - i->rad) { x = xmax - i->rad; vx *= -0.5; }
if (x <= xmin + i->rad) { x = xmin + i->rad; vx *= -0.5; }
if (y > ymax - i->rad) { y = ymax - i->rad; vy*= -0.5; }
if (y < ymin + i->rad) {
y = ymin + i->rad; vy *= -0.5;
vx *= 0.95;
vz *= 0.95;
}
if (z > zmax - i->rad) { z = zmax - i->rad; vz *= -0.5; }
if (z < zmin + i->rad) { z = zmin + i->rad; vz *= -0.5; }
//vy = vy - 0.05; //if you want some "real" gravitation
x += vx;
y += vy;
z += vz;
i->vx = vx;
i->vy = vy;
i->vz = vz;
i->x = x;
i->y = y;
i->z = z;
}
for (vector<P>::iterator i = p.begin(); i != p.end(); ++i)
for (vector<P>::iterator j = p.begin(); j != p.end(); ++j)
{
if (i != j)
{
float d = sqrt((i->x - j->x) * (i->x - j->x) + (i->y - j->y) * (i->y - j->y) + (i->z - j->z) * (i->z - j->z)); //distance
if (d < i->rad + j->rad)
{
float f = (i->rad + j->rad - d);
i->vx += f * (i->x - j->x) / d / i->rad;
i->vy += f * (i->y - j->y) / d / i->rad;
i->vz += f * (i->z - j->z) / d / i->rad;
j->vx -= f * (i->x - j->x) / d / j->rad;
j->vy -= f * (i->y - j->y) / d / j->rad;
j->vz -= f * (i->z - j->z) / d / j->rad;
i->vx *= 0.95;
i->vy *= 0.95;
j->vx *= 0.95;
j->vy *= 0.95;
}
}
}
}
GLvoid DrawGLScene()
{
std::lock_guard<std::mutex> lock(mtx);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
HCURSOR oldCursor = SetCursor(NULL);
GetCursorPos(&mousexy);
angleX += 0.1*(WSCREEN / 2 - mousexy.x);
angleY += 0.1*(HSCREEN / 2 - mousexy.y);
SetCursorPos(WSCREEN / 2, HSCREEN / 2);
if (angleY<-80.0) angleY = -80.0;
if (angleY> 80.0) angleY = 80.0;
if (angleX >= 360) angleX -= 360;
if (angleX < 0) angleX += 360;
glLoadIdentity();
gluLookAt(x, y, z,
x - sin(angleX / 180 * PI),
y + tan(angleY / 180 * PI),
z - cos(angleX / 180 * PI),
0, 1, 0);
glBegin(GL_QUADS);
glColor3f(1.0f, 1.0f, 1.0f);
glVertex3f(xmin, 0, zmin);
glVertex3f(xmax, 0, zmin);
glVertex3f(xmax, 0, zmax);
glVertex3f(xmin, 0, zmax);
glEnd();
for (vector<P>::iterator i = p.begin(); i != p.end(); ++i)
{
glPushMatrix();
glTranslatef(i->x, i->y, i->z);
glColor3ub(i->r, i->g, i->b);
glutSolidSphere(i -> rad, 15, 15);
glPopMatrix();
}
for (vector<P>::iterator i = p.begin(); i != p.end(); ++i)
for (vector<P>::iterator j = p.begin(); j != p.end(); ++j)
{
if (i != j)
{
float d = sqrt((i->x - j->x) * (i->x - j->x) + (i->y - j->y) * (i->y - j->y) + (i->z - j->z) * (i->z - j->z)); //distance
if (d < 200) { line(i->x, i->y, i->z, i->r, i->g, i->b, j->x, j->y, j->z, j->r, j->g, j->b); }
}
}
}
GLvoid InitGL(GLsizei Width, GLsizei Height)
{
glClearColor(0.0f, 0.0f, 0.0f, 0.0f); //Очистка екрана в чорний
glClearDepth(1.0); //Дозволити очистку буфера глибини
glDepthFunc(GL_LESS); //Тип теста глибини
glEnable(GL_DEPTH_TEST); //Дозволити тест глибини
glShadeModel(GL_SMOOTH); //Дозволити плавне згладження
glMatrixMode(GL_PROJECTION); //Вибір матриці проекції
glLoadIdentity(); //Скидання матриці проекції
gluPerspective(45.0f, (GLfloat)Width / (GLfloat)Height, 0.1f, 1000000.0f); //Визначення геометничних відношень для вікна
glMatrixMode(GL_MODELVIEW); //Вибір матриці
}
GLvoid ReSizeGLScene(GLsizei Width, GLsizei Height)
{
if (Height == 0) Height = 1; //Попередження ділення на нуль
glViewport(0, 0, Width, Height);
glMatrixMode(GL_PROJECTION); //Вибір матриці проекції
glLoadIdentity(); //Скид матриці проекції
gluPerspective(45.0f, (GLfloat)Width / (GLfloat)Height, 0.1f, 1000000.0f); //Визначення геометничних відношень для вікна
glMatrixMode(GL_MODELVIEW); //Вибір матриці
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
RECT Screen;
GLuint PixelFormat;
static PIXELFORMATDESCRIPTOR pfd =
{
sizeof(PIXELFORMATDESCRIPTOR),
1,
PFD_DRAW_TO_WINDOW |
PFD_SUPPORT_OPENGL |
PFD_DOUBLEBUFFER,
PFD_TYPE_RGBA,
16,
0, 0, 0, 0, 0, 0,
0,
0,
0,
0, 0, 0, 0,
16,
0,
0,
PFD_MAIN_PLANE,
0,
0, 0, 0
};
switch (message)
{
case WM_CREATE:
hDC = GetDC(hWnd);
PixelFormat = ChoosePixelFormat(hDC, &pfd);
if (!PixelFormat) { MessageBox(0, "Can't Find A Suitable PixelFormat.", "Error", MB_OK | MB_ICONERROR); PostQuitMessage(0); break; }
if (!SetPixelFormat(hDC, PixelFormat, &pfd)) { MessageBox(0, "Can't Set ThePixelFormat.", "Error", MB_OK | MB_ICONERROR);PostQuitMessage(0);break; }
hRC = wglCreateContext(hDC);
if (!hRC) { MessageBox(0, "Can't Create A GL Rendering Context.", "Error", MB_OK | MB_ICONERROR); PostQuitMessage(0); break; }
if (!wglMakeCurrent(hDC, hRC)) { MessageBox(0, "Can't activate GLRC.", "Error", MB_OK | MB_ICONERROR); PostQuitMessage(0); break; }
GetClientRect(hWnd, &Screen);
InitGL(Screen.right, Screen.bottom);
break;
case WM_DESTROY:
case WM_CLOSE:
ChangeDisplaySettings(NULL, 0);
wglMakeCurrent(hDC, NULL);
wglDeleteContext(hRC);
ReleaseDC(hWnd, hDC);
PostQuitMessage(0);
break;
case WM_KEYDOWN:
keys[wParam] = TRUE;
break;
case WM_KEYUP:
keys[wParam] = FALSE;
break;
case WM_SIZE:
ReSizeGLScene(LOWORD(lParam), HIWORD(lParam));
break;
default:
return (DefWindowProc(hWnd, message, wParam, lParam));
}
return (0);
}
void lifeball()
{
for (vector<P>::iterator i = p.begin(); i != p.end();)
{
i->life--;
if (i->life == 0) i = p.erase(i);
else ++i;
}
}
int main() //WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) - if you dont nead a console(but you nead)
{
int counter;
int thread_num;
cout << "\t\t\t" << "Enter number of balls" << endl;
cin >> counter;
cout << "\t\t\t" << "Enter number of threads" << endl;
cin >> thread_num;
vector<thread> thread_array_for_init(thread_num);
vector<thread> thread_array_for_move(thread_num);
HWND hwndC = GetConsoleWindow();
HINSTANCE hInstC = GetModuleHandle(0);
MSG msg;
WNDCLASS wc = {};
HWND hWnd;
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wc.lpfnWndProc = (WNDPROC)WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = GetModuleHandle(0);
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = "OpenGL WinClass";
if (!RegisterClass(&wc)) { MessageBox(0, "Failed To Register The WindowClass.", "Error", MB_OK | MB_ICONERROR); return FALSE; }
hWnd = CreateWindow("OpenGL WinClass", "Jeff Molofee's GL Code Tutorial ... NeHe '99", WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
0, 0, WSCREEN, HSCREEN, NULL, NULL, wc.hInstance, NULL);
if (!hWnd) { MessageBox(0, "Window Creation Error.", "Error", MB_OK | MB_ICONERROR);return FALSE; }
DEVMODE dmScreenSettings;
dmScreenSettings.dmSize = sizeof(DEVMODE);
dmScreenSettings.dmPelsWidth = WSCREEN;
dmScreenSettings.dmPelsHeight = HSCREEN;
dmScreenSettings.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN);
ShowWindow(hWnd, SW_SHOW);
UpdateWindow(hWnd);
SetFocus(hWnd);
while (1)
{
for (auto i = 0; i < thread_num; i++) {
thread_array_for_move[i] = thread(moving, ref(counter));
}
/*for (auto i = 0; i < thread_num; i++) {
cout << "Thread for function move have id: " << thread_array_for_move[i].get_id() << endl;
}*/
DrawGLScene();
SwapBuffers(hDC);
while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
{
if (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); }
else { return TRUE; }
}
if (keys[0x57]) //w
{
x -= (float)sin(angleX / 180 * PI) * Playerspeed;
y += (float)tan(angleY / 180 * PI) * Playerspeed;
z -= (float)cos(angleX / 180 * PI) * Playerspeed;
}
if (keys[0x53])//s
{
x += (float)sin(angleX / 180 * PI) * Playerspeed;
y -= (float)tan(angleY / 180 * PI) * Playerspeed;
z += (float)cos(angleX / 180 * PI) * Playerspeed;
}
if (keys[0x44]) //right
{
x += (float)sin((angleX + 90) / 180 * PI) * Playerspeed;
z += (float)cos((angleX + 90) / 180 * PI) * Playerspeed;
}
if (keys[0x41])//left
{
x += (float)sin((angleX - 90) / 180 * PI) * Playerspeed;
z += (float)cos((angleX - 90) / 180 * PI) * Playerspeed;
}
if (y < 5) y = 5;
if (keys[VK_ESCAPE]) { //ESC
for (auto i = 0; i < thread_num; i++) {
if (thread_array_for_move[i].joinable()) thread_array_for_move[i].join();
}
SendMessage(hWnd, WM_CLOSE, 0, 0);
return 0;
}
for (auto i = 0; i < thread_num; i++) {
if (thread_array_for_move[i].joinable()) thread_array_for_move[i].join();
}
}
}