СП лекция-05ua

Содержание

Слайд 2

Тема лекції: Засоби міжпроцесного взаємодії

Л е к ц і я № 5 по курсу

Тема лекції: Засоби міжпроцесного взаємодії Л е к ц і я №
"Системне програмування "

Змістовний модуль № 1: Системне програмування в Wındows

27.09.2021

Лектор:
Доцент кафедри Інформаційних систем
кандидат технічних наук, доцент
Голубничий Дмитро Юрійович

Слайд 3

НАВЧАЛЬНІ ПИТАННЯ:

НАВЧАЛЬНІ ПИТАННЯ:

Слайд 4

Вступ

Вступ

Слайд 6

Рис. 2. Види станів об'єктів синхронізації

Рис. 2. Види станів об'єктів синхронізації

Слайд 7

1.1. Буфер обміну

1.1. Буфер обміну

Слайд 8

Буфер обміну (clipboard) Windows забезпечує простий обмін даними між додатками

Буфер обміну (clipboard) Windows забезпечує простий обмін даними між додатками

Слайд 9

Приклади визначених форматів буфера обміну

Приклади визначених форматів буфера обміну

Слайд 10

Формат CF_OWNERDISPLAY

Формат CF_OWNERDISPLAY

Слайд 11

1.2. Атоми

1.2. Атоми

Слайд 12

Операції з атомами

1. Збереження рядка в локальній таблиці атомів

ATOM AddAtom

Операції з атомами 1. Збереження рядка в локальній таблиці атомів ATOM AddAtom
(LPCTSTR lpszStringToStore )

2. Зменшення лічильника посилань на атом

ATOM DeleteAtom (ATOM nAtom)

3. Пошук атома

ATOM FindAtom (LPCTSTR lpszString )

4. Повернення імені атома з таблиці атомів

UINT GetAtomName (ATOM nAtom, LPTSTR lpBuffer, int nSize)

5. Встановлення кількості елементів верхнього рівня в локальній таблиці атомів

BOOL InitAtomTable (DWORD nSize)

Amом (atom) являє собою унікальне 16-розрядне значення, яке пов'язане зі строковою константою.
Значення рядка, яке представляє атом, відоме під назвою імені атома (atom name).

Слайд 13

Приклади роботи з атомами

LPCTSTR szAtom = "Atom";
LRESULT CALLBACK WndProc (HWND hWnd,

Приклади роботи з атомами LPCTSTR szAtom = "Atom"; LRESULT CALLBACK WndProc (HWND
UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE:
// Збільшити до 73 число елементів у верхній частині таблиці атомів.
InitAtomTable(73);
break;
case WM_PAINT:
{
// Показати результати пошуку атома.
static PAINTSTRUCT ps;
static char szWorkArea [33];
static char szBuffer [128];
static ATOM aAnAtom;
aAnAtom = INVALID_ATOM;
BeginPaint (hWnd, & ps);
if (aAnAtom = FindAtom(SzAtom))
{
GetAtomName(AAnAtom, szWorkArea, 32);
wsprintf (szBuffer, "атом НЕ знайдений. ", SzWorkArea);
}
else
lstrcpy( szBuffer, "Атом може бути доданий." );

TextOut( ps.hdc, 0, 0, szBuffer, lstrlen( szBuffer ));
EndPaint (hWnd, & ps);
}
break;
case WM_COMMAND:
switch (LOWORD (wParam))
{
case IDM_ADD:
// ввести атом.
AddAtom(SzAtom);
InvalidateRect (hWnd, NULL, TRUE);
break;
case IDM_DELETE:
// Знайти і видалити атом.
if ( FindAtom(SzAtom))
DeleteAtom(FindAtom (szAtom));
InvalidateRect (hWnd, NULL, TRUE);
break;
. . .

char szStoredString [6];
WORD wValue = 100;
ATOM aValue = AddAtom(MAKEINTATOM(wValue));
// значення атома одно 100.
// рядок буде містити число "#100".
GetAtomName( aValue, szStoredString, 6 );

Застосування цілих атомів

Слайд 14

1.3. Канали передачі даних

1.3. Канали передачі даних

Слайд 15

стандарт UNC

Канали (pipe)

дозволяють

Організувати передачу даних між локальними процесами

Організувати

стандарт UNC Канали (pipe) дозволяють Організувати передачу даних між локальними процесами Організувати
передачу даних між процесами, запущеними на різних робочих станціях в мережі

\\ІмяСервера\pipe\Ім'я_Канала

\\.\Pipe\Ім'я_Канала

Стосовно до каналів (в загальному вигляді)

Стосовно до каналів (для одного комп'ютера)

Слайд 16

ФУНКЦІЇ ДЛЯ РОБОТИ З КАНАЛАМИ

ФУНКЦІЇ ДЛЯ РОБОТИ З КАНАЛАМИ

Слайд 17

HANDLE CreateNamedPipe (LPCTSTR lpName, DWORD dwOpenMode, DWORD dwPipeMode, DWORD nMaxInstances, DWORD nOutBufferSize,

HANDLE CreateNamedPipe (LPCTSTR lpName, DWORD dwOpenMode, DWORD dwPipeMode, DWORD nMaxInstances, DWORD nOutBufferSize,
DWORD nInBufferSize, DWORD nDefaultTimeOut, LPSECURITY_ATTRIBUTES lpSecurityAttributes)

Режими роботи каналу

Слайд 18

Приклад 1

Створити іменований канал з ім'ям $MyPipe$

Рішення

// Ідентифікатор каналу

Приклад 1 Створити іменований канал з ім'ям $MyPipe$ Рішення // Ідентифікатор каналу
Pipe
HANDLE hNamedPipe;
// Ім'я створюваного каналу Pipe
LPSTR lpszPipeName = "\\\\. \\ pipe \\ $ MyPipe $";
// Створюємо канал Pipe, що має ім'я lpszPipeName
hNamedPipe = CreateNamedPipe( lpszPipeName,
PIPE_ACCESS_DUPLEX, //режим відкриття каналу
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,//режим роботи
PIPE_UNLIMITED_INSTANCES, // максимальна кількість реалізацій каналу
512, // розмір вихідного буфера в байтах
512, // розмір вхідного буфера в байтах
100, // час очікування в мілісекундах
NULL); // покажчик на атрибути захисту
// Якщо виникла помилка, виводимо її код і зваершаем роботу додатка
if (hNamedPipe == INVALID_HANDLE_VALUE)
{
fprintf (stdout, "CreateNamedPipe: Error% ld \ n",
GetLastError ());
getch ();
return 0;
}

Слайд 19

З'єднання з каналом з боку сервера

Відключення серверного процесу від клієнтського процесу

BOOL

З'єднання з каналом з боку сервера Відключення серверного процесу від клієнтського процесу BOOL DisconnectNamedPipe (HANDLE hNamedPipe)
DisconnectNamedPipe (HANDLE hNamedPipe)

Слайд 20

З'єднання з каналом з боку клієнта

Закриття іменованого каналу

CloseHandle (HANDLE hNamedPipe).

З'єднання з каналом з боку клієнта Закриття іменованого каналу CloseHandle (HANDLE hNamedPipe).

Слайд 21

Приклад 2

Фрагмент клієнтського додатка, який відкриває канал з ім'ям $MyPipe$. Канал

Приклад 2 Фрагмент клієнтського додатка, який відкриває канал з ім'ям $MyPipe$. Канал
відкривається як для запису, так і для читання

char szPipeName [256];
HANDLE hNamedPipe;
strcpy (szPipeName, "\\\\. \\ pipe \\ $ MyPipe $");
hNamedPipe = CreateFile(
szPipeName, GENERIC_READ | GENERIC_WRITE,
0, NULL, OPEN_EXISTING, 0, NULL);

Рішення

Слайд 22

1.4.Поштові канали передачі даних (MailSlot)

1.4.Поштові канали передачі даних (MailSlot)

Слайд 24

Функції для роботи з каналами MailSlot

\\.\Mailslot\[Шлях] Ім'я_Канала

стандарт UNC

Стосовно до поштових

Функції для роботи з каналами MailSlot \\.\Mailslot\[Шлях] Ім'я_Канала стандарт UNC Стосовно до
каналах (всередині мережі)

Стосовно до поштових каналах (всіх комп'ютерів домену)

\\ім'я комп'ютера\Mailslot\[Шлях]Ім'я_Канала

Стосовно до поштових каналах (одночасно всіх станціях мережі)

\\*\Mailslot\[Шлях] Ім'я_Канала

Слайд 25

Приклад 3

LPSTR lpszMailslotName = "\\\\.\\mailslot\\$MailslotName$";
hMailslot = CreateMailslot (lpszMailslotName, 0, MAILSLOT_WAIT_FOREVER, NULL);

Приклад 3 LPSTR lpszMailslotName = "\\\\.\\mailslot\\$MailslotName$"; hMailslot = CreateMailslot (lpszMailslotName, 0, MAILSLOT_WAIT_FOREVER,

Створення каналу Mailslot

Відкриття каналу Mailslot

LPSTR lpszMailslotName = "\\\\.\\mailslot\\$MailslotName$";
hMailslot = CreateFile (lpszMailslotName, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);

Запис повідомлень в канал Mailslot

HANDLE hMailslot;
char szBuf[512];
DWORD cbWritten;
WriteFile (hMailslot, szBuf, strlen (szBuf) + 1, & cbWritten, NULL);

Читання повідомлень з каналу Mailslot

HANDLE hMailslot;
char szBuf [512];
DWORD cbRead;
ReadFile (hMailslot, szBuf, 512, & cbRead, NULL);

Слайд 26

1.5.Передача повідомлень між процесами

1.5.Передача повідомлень між процесами

Слайд 27

Передача даних не вимагає синхронізації

Метод заснований на передачі повідомлення WM_COPYDATA за

Передача даних не вимагає синхронізації Метод заснований на передачі повідомлення WM_COPYDATA за
допомогою функції SendMessage()

wParam - ідентифікатор вікна, що посилає повідомлення.

lParam - покажчик на попередньо заповнену структуру COPYDATASTRUCT

typedef struct tagCOPYDATASTRUCT
{
DWORD dwData; //32-розрядні дані
DWORD cbData; // розмір переданого буфера з даними
PVOID lpData; // покажчик на буфер з даними
} COPYDATASTRUCT;

Слайд 28

1.6.Колективна пам'ять

1.6.Колективна пам'ять

Слайд 29

Колективна пам'ять є собою сегмент фізичної пам'яті, відображеної в віртуальному адресному просторі

Колективна пам'ять є собою сегмент фізичної пам'яті, відображеної в віртуальному адресному просторі
двох або більше процесів.

Схема створення відображення файлу

HANDLE CreateFileMapping (HANDLE hFile, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwflProtect, DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow, LPCTSTR lpName)

LPVOID MapViewOfFile (HANDLE hFileMappingObject, DWORD dwDesiredAccess, DWORD dwFileOffsetHigh, DWORD dwFileOffsetLow, DWORD dwNumberOfBytesToMap)

Слайд 30

2. Синхронізація потоків в режимі ядра.

2.1. Блокуючі змінні

2. Синхронізація потоків в режимі ядра. 2.1. Блокуючі змінні

Слайд 31

Атомарний доступ (Atomic access) - монопольний захоплення ресурсу потоком, що до нього

Атомарний доступ (Atomic access) - монопольний захоплення ресурсу потоком, що до нього
звертається

Приклад № 1

long x = 0;
DWORD WINAPI ThreadFunc1 (PVOID pvParam)
{
x ++;
return (0);
}
DWORD WINAPI ThreadFunc2 (PVOID pvParam)
{
x ++;
return (0);
}

Питання. Чому дорівнює значення глобальної змінної x?

Слайд 32

Сімейство Interlocked-функцій

LONG InterlockedExchange (LPLONG lpTarget, LONG lNewVal);

LONG InterlockedExchangeAdd (PLONG

Сімейство Interlocked-функцій LONG InterlockedExchange (LPLONG lpTarget, LONG lNewVal); LONG InterlockedExchangeAdd (PLONG pAddend,
pAddend, LONG lIncrement);

LONG InterlockedIncrement (LPLONG lpAddend);

LONG InterlockedDecrement (LPLONG lpAddend);

PVOID InterlockedCompareExchange(PLONG pIDestination, LONG lExchange, LONG lComparand);

PVOID InterlockedCompareExchangePointer (PVOID * ppvDestination, PVOID pvExchange, PVOID pvComparand)

1. Обмін однієї змінної на значення типу LONG

2. Додавання однієї змінної з іншого

3. Збільшення на 1 змінної

4. Зменшення на 1 змінної

5. Якщо pIDestination = lCommand, то * pIDestination буде lExchange

6. аналог функціїInterlockedCompareExchange для 64 розрядних

Слайд 33

Сімейство Interlocked-функцій

Приклад № 2

// визначаємо глобальну змінну
long g_x =

Сімейство Interlocked-функцій Приклад № 2 // визначаємо глобальну змінну long g_x =
0;
DWORD WINAPI ThreadFunc1 (PVOID pvParam)
{ InterlockedExchangeAdd (& g_x, 1); return (0);
}
DWORD WINAPI ThreadFunc2 (PVOID pvParam)
{ InterlockedExchangeAdd (& g_x, 1); return (0);
}

// змінна типу
//LONG, використовувана //декількома потоками LONG g_x;
// неправильний спосіб //збільшення змінної //типу LONG g_x ++;
// правильний спосіб //збільшення змінної //типу LONG InterlockedExchangeAdd (& g_x, 1);

Слайд 34

Сімейство Interlocked-функцій

Приклад № 3
Використання Interlocked-функцій при спін-блокуванні (spinlock)

// глобальна змінна,

Сімейство Interlocked-функцій Приклад № 3 Використання Interlocked-функцій при спін-блокуванні (spinlock) // глобальна
яка використовується як індикатор // того, чи зайнятий розділяється ресурс BOOL g_fResourceInUse = FALSE;
...
void Func1 ()
{
// чекаємо доступу до ресурсу
while (InterlockedExchange (& g_fResourceInUse, TRUE) == TRUE) Sleep (0);
...
// отримуємо ресурс в своє розпорядження
// доступ до ресурсу більше не потрібен InterlockedFxchange (& g_fResourceInUse, FALSE);
}

Слайд 35

2. Синхронізація потоків в режимі ядра.

2.2. Критичні секції

2. Синхронізація потоків в режимі ядра. 2.2. Критичні секції

Слайд 36

Критичний розділ - це блок коду, при виконанні якого потік не може

Критичний розділ - це блок коду, при виконанні якого потік не може
бути перерваний.

Критичні секції можна використовувати для синхронізації завдань, створених різними процесами.

Слайд 37

1. Ініціалізація

VOID InitializeCriticalSection (LPCRITICAL_SECTION lpCrSect);

2. Вхід в критичну секцію

VOID EnterCriticalSection (LPCRITICAL_SECTION

1. Ініціалізація VOID InitializeCriticalSection (LPCRITICAL_SECTION lpCrSect); 2. Вхід в критичну секцію VOID
lpCrSect) ;

3. Вихід з критичної секції

VOID LeaveCriticalSection (LPCRITICAL_SECTION lpCrSect) ;

4. Видалення критичної секції

VOID DeleteCriticalSection (LPCRITICAL_SECTION lpCrSect) ;

ОПЕРАЦІЇ З КРИТИЧНИМИ РОЗДІЛАМИ

Слайд 38

Приклад № 4. Використ ання крит ичної секції

EnterCriticalSection (& cs);
hdc = BeginPaint

Приклад № 4. Використ ання крит ичної секції EnterCriticalSection (& cs); hdc
(hWnd, & ps);
GetClientRect (hWnd, & rc);
DrawText (hdc, "SDI Window", -1, & rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
EndPaint (hWnd, & ps);
LeaveCriticalSection (& cs);

Слайд 39

3. Синхронізація потоків у виконавчій системі

3.1. Таймери очікування

3. Синхронізація потоків у виконавчій системі 3.1. Таймери очікування

Слайд 40

Таймери очікування (waitable timers) - це об'єкти ядра, які самостійно переходять у

Таймери очікування (waitable timers) - це об'єкти ядра, які самостійно переходять у
вільний стан в певний час або через регулярні проміжки часу.

1. Створення таймера

HANDLE CreateWaitableTimer(LPSECURITY_ATTRIBUTES lpTimerAttrib, BOOL fManualReset, LPCTSTR lpszName);

2. Отримання дескриптора таймера

HANDLE OpenWaitableTimer(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCTSTR lpszName);

3. Створення копії дескриптора об'єкта

BOOL DuplicateHandle (HANDLE hSourceProcess, HANDLE hSourceObject, HANDLE hTargetProcess, LPHANDLE lphTarget, DWORD dwAccessFlags, BOOL, DWORD dwOptions);

4. Перехід у вільний стан

BOOL SetWaitableTimer(HANDLE hTimer, Const LARGE_INTEGER *pDueTime, LONG lPeriod, PTIMERAPCROUTINE pfnCompletionRoutine, PVOID pvArgToCompletionRoutine, BOOL fResume);

5. Видалення таймера

BOOL CancelWaitableTimer(HANDLE hTimer);

Слайд 41

Приклад № 5. Використання таймерів очікування

година:хвилина:секунда:мілісекунда

Приклад № 5. Використання таймерів очікування година:хвилина:секунда:мілісекунда

Слайд 42

Приклад № 6. Спрацьовування таймера через 5 сек.

// оголошуємо свої локальні змінні

Приклад № 6. Спрацьовування таймера через 5 сек. // оголошуємо свої локальні

HANDLF hTimer;
LARGE_INTEGER li;
// створюємо таймер з автоскиданням
hTimer = CreateWaitableTimer (NULL, FALSE, NULL);
// таймер повинен спрацювати через 5 секунд після виклику SetWaitableTimer;
// задаємо час в інтервалах по 100 нс
const int nTimerUnitsPerSecond = 10000000;
// робимо отримане значення негативним, щоб // SetWaitableTimer знав:
// нам потрібен відносний, а не абсолютний час
li.QuadPart = - (5 * nTimerUnitsPerSecond);
// встановлюємо таймер (він спрацьовує спочатку через 5 секунд, а потім через кожні 6 годин)
SetWaitableTimer (hTimer, &li, 6 * 60 * 60 * 1000, NULL, NULL, FALSE);
...

Слайд 43

4. Синхронізація потоків в призначеному для користувача режимі

4.1. Семафори

4. Синхронізація потоків в призначеному для користувача режимі 4.1. Семафори

Слайд 45

Функції для роботи з семафора

1. Створення семафора

HANDLE CreateSemaphore (LPSECURITY_ATTRIBUTES lpThreadSecurity,

Функції для роботи з семафора 1. Створення семафора HANDLE CreateSemaphore (LPSECURITY_ATTRIBUTES lpThreadSecurity,
LONG lSemInitialCount, LONG lSemMaxCount, LPCTSTR lpszSemName);

2. Відкриття семафора

HANDLE OpenSemaphore (DWORD dwAccessFlag, BOOL bInherit, LPCTSTR lpszSemName);

3. Збільшення значення лічильника семафора

BOOL ReleaseSemaphore (HANDLE hSemaphore, LONG lReleaseCount, LPLONG lpPreviosCount);

4. Зменшення значення лічильника семафора (функції очікування)

DWORD WaitForSingleObject (HANDLE handle, DWORD timeout);

DWORD WaitForMultipleObjects (DWORD count, CONST HANDLE *handles, BOOL waitall, DWORD timeout);

Слайд 46

DWORD WINAPI ThreadProc (LPDWORD lpData)
{
TCHAR szBuffer [256];
DWORD dwSemCount =

DWORD WINAPI ThreadProc (LPDWORD lpData) { TCHAR szBuffer [256]; DWORD dwSemCount =
0;
HWND hList = (HWND) lpData;
HANDLE hSemaphore = OpenSemaphore (SYNCHRONIZE | SEMAPHORE_MODIFY_STATE, FALSE, lpszSemaphore);
wsprintf (szBuffer, "Thread% x waiting for semaphore% x", GetCurrentThreadId (), hSemaphore);
SendMessage (hList, LB_INSERTSTRING, (WPARAM) -1, (LPARAM) szBuffer);
// Перевірка стану семафора.
WaitForSingleObject (hSemaphore, INFINITE);
wsprintf (szBuffer, "Thread% x got semaphore", GetCurrentThreadId ());
SendMessage (hList, LB_INSERTSTRING, (WPARAM) -1, (LPARAM) szBuffer);
Sleep (5000);
// Видалення семафора.
ReleaseSemaphore (hSemaphore, 1, & dwSemCount);
wsprintf (szBuffer, "Thread% x is done with semaphore. Its count was% ld.", GetCurrentThreadId (), dwSemCount);
SendMessage (hList, LB_INSERTSTRING, (WPARAM) -1, (LPARAM) szBuffer);
CloseHandle (hSemaphore);
return (0);
}

приклад № 6. Використання семафорів

Слайд 47

4. Синхронізація потоків в призначеному для користувача режимі

4.2. М'ютекси

4. Синхронізація потоків в призначеному для користувача режимі 4.2. М'ютекси

Слайд 48

4.2.М'ЮТЕКСИ І ПОДІЇ

М'ютекс (mutex, mutual exclusion — взаємне виключення) призначено для захисту

4.2.М'ЮТЕКСИ І ПОДІЇ М'ютекс (mutex, mutual exclusion — взаємне виключення) призначено для
певного об’єкта у потоці від доступу інших потоків.
М'ютекс є одним із засобів синхронізації роботи потоків або процесів

М'ютекси — це прості двійкові семафори, які можуть перебувати в одному з двох станів - сигнальному або несигнальному (відкритий і закритий відповідно). Коли потік отримує м'ютекс, той переводиться в несигнальний стан.
Організація послідовного доступу до ресурсів з використанням м'ютексів стає нескладною, оскільки в кожен конкретний момент тільки один потік може володіти цим об'єктом. Для того, щоб об'єкт mutex став доступний потокам, що належать різним процесам, при створенні йому необхідно присвоїти ім'я. Потім це ім'я потрібно передати «у спадок» завданням, які повинні його використовувати для взаємодії.

Слайд 49

1. Створення об'єкта Mutex

HANDLE CreateMutex (LPSECURIT_ATTRIBUTES lpSecurityAttribs, BOOL bInitialOwner, LPCTSTR lpszMutexName);

1. Створення об'єкта Mutex HANDLE CreateMutex (LPSECURIT_ATTRIBUTES lpSecurityAttribs, BOOL bInitialOwner, LPCTSTR lpszMutexName);

3. відкриття об'єкту Mutex

HANDLE OpenMutex (DWORD dwAccessFlag, BOOL bInherit, LPCTSTR lpszMutexName);

5. звільнення об'єкта Mutex

BOOL ReleaseMutex (HANDLE hMutex)

Операції з об'єктом Mutex

2. Звільнення дескриптора об'єкта Mutex

BOOL CloseHandle (HANDLE hObject);

4. Зменшення значення лічильника семафора

DWORD WaitForSingleObject (HANDLE handle, DWORD timeout);

DWORD WaitForMultipleObjects (DWORD count, CONST HANDLE *handles, BOOL waitall, DWORD timeout);

Слайд 50

4. Синхронізація потоків в призначеному для користувача режимі

4.3. Події

4. Синхронізація потоків в призначеному для користувача режимі 4.3. Події

Слайд 51

Схема використання подій

Схема використання подій

Слайд 52

Операції з об'єктом - подія

1. Створення об'єкта Event

HANDLE CreateEvent (LPSECURIT_ATTRIBUTES

Операції з об'єктом - подія 1. Створення об'єкта Event HANDLE CreateEvent (LPSECURIT_ATTRIBUTES
lpEventSecurity, BOOL bManualReset, BOOL bInitialState, LPCTSTR lpszEventName)

2. Встановлення вільного стану об'єкта Event

BOOL SetEvent (HANDLE hEvent)

3. Встановлення зайнятого стану об'єкта Event

BOOL ResetEvent (HANDLE hEvent)

4. Установка / скидання стану об'єкта Event

BOOL PulseEvent (HANDLE hEvent)