ЕКЗАМЕНАЦІЙНИЙ КВИТОК № 6
1. Спільне використання об'єктів ядра кількома процесами.
2. Критичні секції та захист даних.
3. Анонімні канали. Іменовані канали.
Анонімні канали Win32 дозволяють проводити одностороннє
(напівдуплексне), символьно-орієнтоване міжпроцесне
взаємодія. Кожен канал має два дескриптори: дескриптор читання та
дескриптор запису. Для створення каналу є наступна функція
BOOL CreatePipe (PHANDLE phRead, PHANDLE phWrite,
LPSECURITY_ATTRIBUTES lpsa, DWORD cbPipe)
Щоб використовувати канал для IPC, потрібен ще один процес, з яким
буде пов'язаний один із дескрипторів каналу.
Читання з дескриптора читання каналу блокується, якщо канал порожній. У
в іншому випадку зчитується стільки байтів, скільки є в каналі, але не
перевищуючи кількість, вказану під час виклику ReadFile. Також блокується
операція запису заповнений канал, який реалізується в буфері пам'яті.
Крім усього іншого, анонімні канали пропускають дані лише в одну
бік. Для двонаправленого зв'язку потрібні два канали.
Іменовані канали мають ряд особливостей, які роблять їх
зручним засобом реалізації додатків із використанням IPC.
Особливості іменованих каналів (деякі їх присутні не завжди)
• Іменовані канали орієнтовані на повідомлення, що читає
передаються записуючим процесом.
• Іменовані канали двонаправлені, так що два процеси можуть
обмінюватися повідомленнями по тому самому каналу.
• Можливо кілька незалежних екземплярів іменованого каналу.
Наприклад, кілька клієнтів можуть зв'язуватися з одним сервером,
використовуючи один і тойж канал, і сервер може відповісти клієнту по тому
ж екземпляру каналу.
• До каналу імені можуть звертатися системи в мережі. Зв'язок через
іменований канал не залежить від того, чи працюють обидва процеси на одній
машині чи різних.
• Існує кілька функцій-напівфабрикатів, які спрощують
взаємодія іменованим каналом і з'єднання клієнта з сервером.
Використання іменованих каналів
Функція CreateNamedPipe створює перший екземпляр іменованого
каналу та повертає його дескриптор. Також ця функція визначає
максимальна кількість екземплярів каналу і, отже, кількість
клієнтів, яких можна обслуговувати одночасно.
Зазвичай процес створення називається сервером. Процеси-клієнти,
можливо, що працюють на інших системах, відкривають цей канал за допомогою
Створення іменованих каналів
Нижче наведено функцію CreateNamedPipe.
HANDLE CreateNamedPipe (LPCTSTR lpszPipeName,
DWQRD fdwOpenMode, DWORD fdwPipeMode,
DWORD nMaxInstances, DWORD cbOutBuf,
DWORD cblnBuf, DWORD dwTimeOut,
lpszPipeName позначає ім'я каналу у формі: \\.\pipe\[шлях]ім'я-каналу
Крапка позначає локальну машину; інакше кажучи, створити канал на
віддалену машину не можна.
fdwOpenMode має одне з наступних значень:
- PIPE_ACCESS_DUPLEX - еквівалентно комбінації GENERIC_READ
- PIPE_ACCESS_INBOUND - напрямок даних тільки від клієнта до
сервера, еквівалентно GENERIC_READ;
- PIPE_ACCESS_OUTBOUND – еквівалентно GENERIC_WRITE.
Режим також може бути FILE_FLAG_WRITE_THROUGH (не використовується
для каналів повідомлень) та FILE_FLAG_OVERLAPPED.
fdwPipeMode може прийматитри взаємовиключні пари прапори.
Вони вказують, чи запис орієнтований на повідомлення або
байтовою, чи здійснюється читання за повідомленнями чи за блоками та
чи блокується операція читання.
- PIPE_TYPE_BYTE та PIPE_TYPE_MESSAGE, які взаємно виключають
один одного, вказують, чи записуються дані в канал як потік байтів
або як потік повідомлень. Для всіх екземплярів каналу застосовується один і
- PIPE_READMODE_BYTE та PIPE_READMODE_MESSAGE вказують,
читаються дані як потік байтів чи як потік повідомлень. Для
PIPE_READMODE_MESSAGE потрібно PIPE_TYPE_MESSAGE.
- PIPE_WAIT та PIPE_NOWAIT визначають, чи буде операція ReadFile
блокуватись. Вказуйте значення PIPE_WAIT, оскільки для
асинхронного введення-виводу є кращі способи.
nMaxInstances визначає кількість екземплярів каналу та,
отже, кількість клієнтів, що одночасно обслуговуються. Якщо
вказати значення PIPE_UNLIMITED_ INSTANCES, кількість каналів буде
визначати ОС залежно від системних ресурсів.
cbOutBuf і cbInBuf задають розміри в байтах буферів введення та виведення,
що використовуються для іменованих каналів. Якщо вказати нуль, будуть
використовувати значення за замовчуванням.
dwTimeOut - прийнятий за замовчуванням тайм-аут (у мілісекундах) для
функції WaitNamedPipe. Ця ситуація, в якій функція створення
визначає тайм-аут для іншої функції, що є унікальною.
Значення, що повертається у разі помилки — INVALID_HANDLE_VALUE,
оскільки дескриптори каналів подібні до дескрипторів файлів.
lpsa відіграє ту ж роль, що і в інших функціях створення.
Під час першого виклику CreateNamedPipe фактичностворюється іменований
канал, а не просто екземпляр. При закритті останнього дескриптора
екземпляра сам цей екземпляр видаляється. Видалення останнього екземпляра
іменованого каналу викликає видалення самого каналу.
Підключення клієнта до іменованого каналу
Клієнт може підключитися до іменованого каналу за допомогою дзвінка
CreateFile із зазначенням імені каналу. У багатьох випадках клієнт та сервер
знаходяться на одній машині; тоді ім'я має вигляд: \\.\pipe\[шлях]ім'я_каналу
Якби сервер знаходився на іншій машині, ім'я виглядало б так:
Якщо сервер локальний, застосування для імені символу "." замість
вказівки імені самої машини значно підвищує швидкодію.
Функції стану іменованого каналу
GetNamedPipeHandleState повертає для цього відкритого
дескриптора відомості про те, чи знаходиться канал у блокованому або
неблокованому режимі, орієнтований він на повідомлення або на байти,
кількості екземплярів каналу тощо.
SetNamedPipeHandleState дозволяє програмі задавати ці атрибути
GetNamedPipelnfo визначає, чи пов'язаний дескриптор з клієнтом або
сервером, який розмір буфера тощо.
Функції підключення іменованого каналу
Після створення екземпляра іменованого каналу сервер може відстежувати
підключення клієнта (за допомогою функцій CreateFile або CallNamedPipe),
використовуючи функцію ConnectNamedPipe.
BOOL ConnectNamedPipe ( HANDLE hNamedPipe,
Якщо встановити в lpo значення NULL, виконання ConnectNamedPipe
завершиться одразу, як тільки з'явиться підключення клієнта. Зазвичай
функція повертає TRUE. Значення FALSE можливо, якщо клієнт
підключається між дзвінкамисервером функцій CreateNamedPipe та
ConnectNamedPipe. У цьому випадку GetLastError повертає значення
за допомогою ReadFile і записувати відповіді за допомогою WriteFile. На закінчення
сервер повинен викликати функцію DisconnectNamedPipe, щоб звільнити
дескриптор (примірник каналу) для з'єднання з іншим клієнтом.
Остання функція, WaitNamedPipe, призначена для синхронізації
підключення клієнта. Ця функція завершується, якщо на сервері є
незавершене звернення до ConnectNamedPipe. Використовуючи
WaitNamedPipe, клієнт може переконатися, що сервер готовий до підключення,
після чого викликати CreateFile. Крім того, виклик сервером функції
ConnectNamedPipe у цьому випадку завершиться успішно.
З'єднання клієнта та сервера через іменований канал
Спочатку в послідовності сервера здійснюється підключення
клієнта, з яким сервер зв'язується доти, доки той не розірве
з'єднання, потім сервер закриває підключення на стороні сервера та
поєднується з іншим клієнтом.
Далі наводиться послідовність підключення клієнта, у якій
клієнт закривається, коли завершує роботу, дозволяючи іншому клієнту
з'єднатися через той самий екземпляр іменованого каналу. Клієнт може
з'єднуватися із сервером через мережу, якщо йому відоме ім'я сервера.
Зверніть увагу, що між клієнтом та сервером відбувається змагання.
По-перше, виклик клієнтом WaitNamedPipe завершиться невдало, якщо
сервер ще створив іменований канал. По-друге, можливі
обставини, коли клієнт може завершити свій виклик CreateFile
перш ніж сервер викличе ConnectNamedPipe. В цьому випадку
ConnectNamedPipe повернеFALSE, але зв'язок через іменований канал
буде працювати, як належить.
Транзакційні функції іменованого каналу
Клієнт виконує наступне:
• відкриває екземпляр каналу, створюючи довготривале підключення до
серверу та споживаючи цей екземпляр каналу;
• поперемінно надсилає запити та чекає на відповіді;
Звичайну послідовність функцій WriteFile, ReadFile можна
вважати окремою транзакцією клієнта, і Win32 є така функція для
BOOL TransactNamedPipe ( HANDLE hNamedPipe,
LPVOID lpvWriteBuf, DWORD cbWriteBuf,
LPVOID lpvReadBuf, DWORD cbReadBuf,
LPDWORD lpcbRead, LPOVERLAPPED lpa)
Призначення параметрів не потребує пояснення, оскільки ця функція
являє собою поєднання WriteFile та ReadFile для дескриптора
іменованого каналу. Вказуються як вихідний, і вхідний буфери, а
*lpcbRead задає довжину повідомлення.
Функція TransactNamedPipe зручна, але потребує постійного
підключення, що обмежує кількість клієнтів.
CallNamedPipe, друга функція-напівфабрикат, позбавлена цього недоліку,
тому що поєднує в собі всю послідовність: CreateFile, WriteFile,
Перевага полягає у більш ефективному використанні каналу,
незважаючи на витрати підключення при кожному запиті.
BOOL CallNamedPipe (LPCPSTR lpszPipeName,
LPVOID lpvWriteBuf, DWORD з bWriteBuf,
LPVOID lpvReadBuf, DWORD зbReadBuf,
LPDWORD lpcbRead, DWORD dwTimeOut)
Використання параметрів подібно до TransactactNamedPipe з тим
винятком, що з каналу вказується ім'я, а чи не дескриптор. Функція
CallNamedPipe є синхронною.
Тут також вказується тайм-аут умілісекундах - для підключення, але
не для транзакції. Для dwTimeOut існує також три спеціальні
• NMPWAIT_USE_DEFAULT_WAIT — у цьому випадку використовується тайм-аут
за замовчуванням, заданий під час виклику CreateNamedPipe.
Спостереження за повідомленнями в іменованому каналі
Крім читання іменованого каналу за допомогою ReadFile, можна також
визначати, чи є у ньому повідомлення, яке вважатимуться. Для
це служить функція PeekNamedPipe.
BOOL PeekNamedPipe (HANDLE hPipe, LPVOID lpvBuffer,
DWORD cbBuffer, LPDWQRD lpcbRead,
LPDWORD lpcbAvail, LPDWORD lpcbMessage)
Функція PeekNamedPipe читає всі байти або повідомлення в каналі, не
руйнуючи їх; вона не блокується та завершується негайно.
Щоб дізнатися, чи є дані у каналі, перевірте значення *lpcbAvail;
якщо є, *lpcbAvail буде більшим за нуль. У цьому випадку lpvBuffer та
lpcbRead може мати значення NULL. ЯКЩО буфер заданий параметрами
lpvBuffer і cbBuffer, то *lpcbMessage повідомляє, чи є байти, що залишилися.
повідомлення, які не вписуються в буфер, що дозволяє виділити великий
буфер перед читанням із іменованого каналу. У байтовому режимі каналу це
Ще раз підкреслимо, що PeekNamedPipe не знищує повідомлення, так
що для видалення повідомлень або байтів із каналу потрібен додатковий
Безпека іменованого каналу
Нижче наведено важливі права безпеки для іменованих каналів.
• SYNCHRONIZE (дозволяє очікувати потоку).
Відповідні права встановлюються залежно від режиму доступу
(Дуплексний, вхідний або вихідний). Для всіх трьох режимів потрібно