Примітиви передавання повідомлень



Основна особливість передавання повідомлень полягає в тому, що процеси спільно використовують тільки канали. Немає необхідності забезпечувати взаємне виключення процесів під час доступу до спільно використовуваних даних, замість цього досить визначити примітиви передавання повідомлень – спеціальні операції обміну даними через канал, які забезпечують не лише обмін даними, але й синхронізацію.

Є два примітиви передавання повідомлень: send (для відсилання повідомлення каналом) і receive (для отримання повідомлення з каналу).

Розглянемо, як особливості реалізації send і receive дають змогу виділити різні класи методів передавання повідомлень.

Зазначені примітиви передавання повідомлень можуть задавати прямий і непрямий обмін даними. При прямому обміні даними необхідно явно вказувати процес, з яким необхідно обмінюватися інформацією. Непрямий обмін здійснюють через спеціальний об’єкт (поштову скриньку, порт); процеси можуть поміщати повідомлення в поштову скриньку і отримувати їх звідти. Зазвичай кілька процесів мають доступ до однієї поштової скриньки, застосовуючи під час її пошуку методи іменування. Більшість сучасних технологій обміну повідомленнями використовує непрямий обмін даними. Прикладом прямого обміну є традиційні сигнали.

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

Можна виокремити різні групи методів передавання повідомлень залежно від того, як вони дають можливість відповісти на два запитання.

1. Чи може потік бути призупинений під час виконання операції send, якщо повідомлення не було отримане?

2. Чи може потік бути призупинений під час виконання операції receive, якщо повідомлення не було відіслане?

У реальних системах відповідь на друге запитання практично завжди буде позитивною – неблокувальне приймання повідомлень спричиняє те, що вони губляться. Варіанти відповідей на перше запитання визначають два класи передавання повідомлень – синхронне і асинхронне.

Під час синхронного передавання повідомлень операція send призупиняє процес до отримання повідомлення, а під час асинхронного передавання повідомлень вона не призупиняє процес (тобто є неблокувальною); після відсилання повідомлення процес продовжує своє виконання, не чекаючи отримання результату. Найзручніше в цьому випадку використати непряму адресацію через поштові скриньки. Реалізація синхронного й асинхронного передавання повідомлень залежить від низки характеристик каналу й обміну повідомленнями, насамперед від пропускної здатності каналу.

· Якщо пропускна здатність дорівнює нулю (повідомлення не можуть очікувати в системі), відправник завжди має очікувати, поки одержувачу не надійде повідомлення, а одержувач має очікувати, поки повідомлення йому не буде відіслано. Два процеси мають явно домовлятися про майбутній обмін.

· Якщо пропускна здатність обмежена (у системі можуть перебувати максимум n повідомлень для цього каналу), відправник має очікувати тільки тоді, коли черга повідомлень для цього каналу переповнена (у ній перебуває рівно n повідомлень), одержувач має очікувати, якщо ця черга порожня.

· Якщо пропускна здатність необмежена, очікування можливе тільки для одержувача за порожньої черги.

Під час обміну повідомленнями необхідне підтвердження їх отримання. Деякі методи обміну повідомленнями не вимагають підтвердження зовсім, в інших випадках можлива ситуація, коли відправника після виконання операції send блокують доти, поки одержувач не надішле йому інше повідомлення із підтвердженням отримання; таку технологію називають обміном повідомленнями із підтвердженням отримання.

Розв’язання задачі виробників-споживачів за допомогою повідомлень

Розглянемо розв’язання задачі виробників-споживачів із використанням асинхронного передавання повідомлень. Організовують дві поштові скриньки – для виробника і для споживача. Якщо скринька порожня, потік очікуватиме, поки в ній не з’явиться повідомлення.

Поштові скриньки виробника та споживача мають свої особливості.

Скринька виробника може містити тільки порожні повідомлення загальною кількістю не більше n. Наявність m повідомлень у цій скриньці служить сигналом для виробника, що в буфері є місце для m об’єктів. Щоб відіслати дані в буфер, виробник забирає зі скриньки одне повідомлення, заповнює його даними і відсилає в скриньку споживача. Заповнивши буфер, виробник спустошить свою скриньку і буде змушений чекати, поки споживач не помістить у неї порожнє повідомлення.

Повідомлення у скриньці споживача відповідають об’єктам у буфері. Після того як споживач забере повідомлення з цієї скриньки, він використає його дані й відішле порожнє повідомлення у скриньку виробника, сигналізуючи, що в буфері з’явилося місце. Порожня скринька споживача означає порожній буфер – споживач чекатиме, поки виробник не помістить заповнене повідомлення в цю скриньку.

На початку роботи скриньку виробника заповнюють порожніми повідомленнями загальним числом n (це буде означати, що він може зробити n об’єктів).

Функції producer() і consumer() схожі. І виробник, і споживач у циклі намагаються забрати повідомлення із своїх скриньок. Якщо це вдається виробникові, він заповнює повідомлення даними і відсилає його у скриньку споживача, якщо це зможе зробити споживач, він скористається повідомленням і відішле порожнє повідомлення у скриньку виробника. Після цього цикл триває.

Ось алгоритм розв’язання цієї задачі:

message_t null_nsg;                                                 //порожнє повідомлення

mailbox_t producer_mailbox, consumer_mailbox;      //поштові скриньки

int n = 100;                                                               //розмір буфера

void producer() {

message_t producer_msg;

for (; ;) {

//забрати повідомлення зі скриньки виробника,

//чекати, якщо вона порожня

receive(consumer_mailbox, producer_msg);

producer_msg.data = produce();

//відіслати заповнене повідомлення у скриньку споживача

send(consumer_mailbox, producer_msg);

}

}

void consumer() {

message_t consumer_msg;

for (; ;) {

//забрати повідомлення зі скриньки споживача чекати якщо вона порожня

receive(consumer_mailbox, consumer_msg);

consume(consumer_msg.data);

//відіслати у скриньку виробника порожнє повідомлення

//повідомивши йому про те, що у буфері є місце

send(producer_mailbox, null_msg);

}

}

void main () {

//заповнити скриньку виробника порожніми повідомленнями

for (int i=0; i<n; i++)

send(producer_mailbox, null_msg);

//запустити producer() і consumer() паралельно

}

Основна відмінність цього розв’язання від запропонованих для потоків полягає в тому, що воно не залежить від спільно використовуваних даних. Доступ до поштових скриньок може бути виконаний за допомогою системних викликів, що приховують їхнє місцезнаходження; скриньки можуть бути й віддаленими. Це дає змогу застосовувати алгоритм тоді, коли виробник і споживач є різними процесами, а можливо, й перебувають на різних комп’ютерах.


Дата добавления: 2018-04-05; просмотров: 507; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!