Лекції з WinApi

Тема №5 Взаємодія процесів

5. Взаємодія процесів
 5.1. Обмін даними між процесами

Win32® API надає багатий набір засобів обміну даними.

Interprocess communications (IPC) - складова частина Win32® API, набір засобів для організації обміну і поділу даних між процесами.

При розробці додатка в середовищі Win32® необхідно задатися питанням, необхідно чи передбачити можливість обміну даними цього додатка з іншими додатками і якими додатковими умовами накладаються на спосіб цього обміну.

  1. Чи буде додаток спілкуватися з іншими тільки на локальному чи комп'ютері необхідно передбачити взаємодію в мережі?
  2. Чи буде додаток спілкуватися з іншими, працюючими під керуванням іншої операційної системи (interoperable - інтероперабельність)?
  3. Чи буде користувач мати можливість вибору, з яким додатком буде взаємодіяти даний чи додаток додаток саме зробить пошук тих додатків, з якими воно повиннео і може взаємодіяти?
  4. Чи буде додаток взаємодіяти з великою кількістю інших додатків загальноприйнятими способами (типу вирізувати/уставити з буфера обміну) чи буде взаємодіяти своїми специфічними методами?
  5. Чи важлива продуктивність для додатка? Використання складних механізмів взаємодії веде до додаткового і дуже істотним накладним витратам.
  6.  Чи буде додаток Windows-based чи йому досить бути консольним? Багато механізмів взаємодії працюють тільки з Windows-based додатками.

Існують наступні способи поділу даних між процесами:

1.                                                             Відображення проецируємиых файлів (File mapping).

Два процеси відображають той самий об'єкт “проецируємий файл” на свій адресний простір. Цей механізм докладно описаний у п. 4.5.3.

2.                                                             Поділювана пам'ять (Shared Memory).

Відрізняється від попереднього способу тільки тим, що як поділюваний файл використовується частина файлу підкачування. Так само описано в пункті 4.5.3.

3.                                                             Безіменні транспортери (Anonymous Pipe).

Один із класичних способів взаємодії процесів, відомий ще в UNIX. За допомогою функції CreatePipe процес створює два описувача: один - для читання, іншої - для запису. Ці описувачі аналогічні описувачам файлів у тім змісті, що з ними можна працювати файловими функціями ReadFile і WriteFile. Якщо породити дочірній процес так, що б він успадковував описувачі, то можна використовувати описувачі транспортера для взаємодії процесів. Можна запропонувати наступну схему взаємодії:

/* Дочірній процес */

#include <stdio.h>

#include <string.h>

int main( void )

{ char WorkString[80] ;

while( strcmp( WorkString, "end" )) {

scanf( "%s", WorkString );

printf( "%s\n", WorkString ) ;

}

return 0 ;

}

/* Батьківський процес */

#include <windows.h>

#include <stdio.h>

#include <conio.h>

int main(void)

{ HANDLE hStandardInput ;

HANDLE hPipeInput ;

HANDLE hPipeOutput ;

PROCESS_INFORMATION pi ;

STARTUPINFO si ;

char WorkString[80] ;

DWORD NumberOfBytesWritten ;

ZeroMemory( &si, sizeof(si)) ;

si.cb = sizeof( si ) ;

hStandardInput = GetStdHandle( STD_INPUT_HANDLE ) ;

CreatePipe( &hPipeInput, &hPipeOutput, NULL, 0 ) ;

SetStdHandle( STD_INPUT_HANDLE, hPipeInput ) ;

CreateProcess( NULL, "Child", NULL, NULL, TRUE, CREATE_NEW_CONSOLE,

NULL, NULL, &si, &pi ) ;

SetStdHandle( STD_INPUT_HANDLE, hStandardInput ) ;

CloseHandle( hPipeInput ) ;

while( strcmp( WorkString, "end\n" )) {

scanf( "%s", WorkString ) ;

strcat( WorkString, "\n" ) ;

WriteFile( hPipeOutput, WorkString, strlen(WorkString),

&NumberOfBytesWritten, NULL ) ;

}

return 0 ;

}

4.                                                             Іменовані транспортери (Named Pipes).

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

5.                                                             Поштові скриньки (MailSlots).

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

6.                                                             Буфер обміну (Clipboard).

Широко застосовуваний у Windows механізм обміну інформацією з використанням спеціального системного глобального буфера. При цьому реалізується модель cut-copy-paste. Будь-який додаток може помістити дані в буфер обміну й одержати дані з нього.

7.                                                             Динамічний обмін даними (DDE - Dynamic Data Exchange).

Механізм, вивчений раніше (див. Методичні вказівки до лабораторної роботи “Використання протоколу DDE при розробці додатків”).

8.                                                             Приєднання і впровадження об'єктів (OLE - Object linking and embedding).

Протокол спочатку призначений для створення складених документів. З його допомогою можна, наприклад, уставити таблицю MS Excel® у документ MS Word. При цьому можливо як уставити таблицю цілком (embedding), так і вставити тільки посилання на цю таблицю (linking). В даний час протокол OLE розвився до набору технологій, що лежать в основі побудови сучасних інформаційних систем.

9.                                                             Динамічно компонуємі бібліотеки (DLL - dynamic link libraries).

Якщо два додатки використовують одну бібліотеку, то вони розділяють усі глобальні перемінні цієї бібліотеки. У дійсності, глобальні перемінні, як і вся бібліотека, відображаються на адресні простори різних процесів. Цей метод не привносить ніякої нової функціональності в порівнянні з відображенням проецируємих файлів і, тому, його використання не рекомендується.

10.                                                          Вилучений виклик процедур (RPC - Remote Procedure Call).

RPC - природний розвиток ідеології процедурного програмування.

Перші програми писалися з використанням операторів goto. Фактично, саме поява таких процедурно орієнтованих мов як З, поклало цьому кінець. Підпрограма стала розглядатися як чорну шухляду з документованими параметрами. Природно спробувати уможливити виконання цієї процедури на вилученому комп'ютері. Механізм здійснення цієї ідеї наступний: з додатком компонується не сама процедура, а лише деяка заглушка, що передає по мережі параметри процедури і приймає результат обчислень. У додатка створюється ілюзія роботи з локальною процедурою. Можливе виконання процедури і зухвалої програми під керуванням різних операційних систем.

5. Взаємодія процесів.
 5.2. Синхронізація потоків.

У середовищі, що дозволяє виконувати одночасно кілька потоків, дуже важливо синхронізувати їхня діяльність. Інакше потоки не зможуть коректно працювати з поділюваними областями даних. Загальна проблематика цього питання вивчена в частині 1 курсу.

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

У Win32® API реалізований класичний механізм критичної секції. Для роботи з критичною секцією необхідно завести структуру CRITICAL_SECTION і ініціалізувати її викликом функціїInitializeCriticalSection. Тепер усі потоки можуть використовувати функції EnterCriticalSection і LeaveCriticalSection для огородження критичного розділу коду. Істотно, що механізм критичних секцій може використовуватися тільки в рамках одного процесу.

  1. Синхронізація потоків різних процесів.

У будь-якому випадку, синхронізація полягає в тому, що потік переводить себе в стан блокованого до настання якого або події. Для перекладу процесу в стан блокування використовуються функції:

  • WaitForMultipleObjects
  • WaitForMultipleObjectsEx
  • WaitForSingleObject
  • WaitForSingleObjectEx

Вихід процесу зі стану блокування відбувається при настанні очікуваної події. Подія складається в зміні стану якого-небудь об'єкта. Практично будь-які об'єкти ядра (процеси, файли, транспортери і т.д.) можуть виступати в ролі синхронізуючих. Але існують об'єкти, призначені саме для синхронізації потоків:

  • Mutex (mutually exclusive - взаємне виключення)
  • Семафори
  • Події

1.                                                             Об'єкти Mutex

Mutex - глобальний іменований об'єкт, що може належати тільки одному потоку. Якщо об'єкт Mutex належить якому-небудь потоку, то інші потоки не можуть захопити його. За допомогою об'єктів Mutex так само реалізується класичний алгоритм критичної секції. Загальна схема така: один з потоків породжує об'єкт Mutex і привласнює йому глобальне ім'я за допомогою функції CreateMutex. Інші потоки можуть використовувати функцію OpenMutex для одержання описувач об'єкта. Перед входом у критичну секцію потік викликає одну з функцій чекання, передаючи їй як параметр описувач об'єкта Mutex. Якщо об'єкт Mutex уже захоплений, то потік блокується до звільнення об'єкта. Якщо не захоплений, то виконання потоку продовжується, а об'єкт Mutex захоплюється. Для звільнення об'єкта Mutex використовується функціяReleaseMutex.

2.                                                             Семафори

За допомогою об'єктів типу семафор реалізується класичний механізм семафорів. При цьому як примітив P(S) виступає одна з функцій чекання, а як примітив V(S) – функція ReleaseSemaphore. При створенні семафора функцією CreateSemaphore можна визначити максимально припустиме значення перемінної S.

3.                                                             Події

Події – самий примітивний різновид синхронізуючих об'єктів. Вони породжуються функцією CreateEvetnt і бувають двох типів “зі скиданням вручну” і “з автоматичним скиданням”. Об'єкт подія може знаходитися в двох станах: “зайнятий” (non-signaled) і “вільний” (signaled). Для перекладу об'єкта подія у вільний стан використовується функція SetEvent, а для перекладу в зайняте – ResetEvent. За допомогою кожної з функцій чекання можна перевести потік, що викликав, у стан блокування до звільнення об'єкта подія. Якщо об'єкт подія є об'єктом “з автоматичним скиданням” то функція чекання автоматично переведе його в стан зайнятого.