logo

Комунікація між процесами (IPC)

Процес може бути двох видів:

  • Самостійний процес.
  • Процес кооперації.

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



  1. Спільна пам'ять
  2. Передача повідомлень

На рисунку 1 нижче показана базова структура зв’язку між процесами через метод спільної пам’яті та через метод передачі повідомлень.

Операційна система може реалізувати обидва методи зв'язку. Спочатку ми обговоримо методи зв’язку зі спільною пам’яттю, а потім передачу повідомлень. Зв'язок між процесами, що використовують спільну пам'ять, вимагає від процесів спільного використання певної змінної, і це повністю залежить від того, як програміст це реалізує. Один із способів зв’язку з використанням спільної пам’яті можна уявити так: припустимо, що процеси1 і процес2 виконуються одночасно, і вони спільно використовують деякі ресурси або використовують інформацію з іншого процесу. Process1 генерує інформацію про певні обчислення або ресурси, що використовуються, і зберігає її як запис у спільній пам’яті. Коли процесу2 потрібно використати спільну інформацію, він перевірить запис, що зберігається у спільній пам’яті, візьме до відома інформацію, згенеровану процесом1, і діятиме відповідно. Процеси можуть використовувати спільну пам'ять для отримання інформації як запису з іншого процесу, а також для доставки будь-якої конкретної інформації іншим процесам.
Давайте обговоримо приклад зв’язку між процесами за допомогою методу спільної пам’яті.



i) Метод спільної пам'яті

Наприклад: Проблема виробник-споживач
Існує два процеси: виробник і споживач. Виробник виробляє деякі предмети, а Споживач споживає цей товар. Два процеси спільно використовують загальний простір або місце пам’яті, відоме як буфер, де зберігається предмет, створений Виробником, і з якого Споживач споживає цей предмет, якщо це необхідно. Є дві версії цієї проблеми: перша відома як проблема необмеженого буфера, в якій виробник може продовжувати виробляти елементи і немає обмежень на розмір буфера, друга відома як проблема обмеженого буфера в які Виробник може виробити до певної кількості товарів, перш ніж він почне чекати, поки Споживач споживе їх. Ми обговоримо проблему обмеженого буфера. Спочатку Виробник і Споживач матимуть спільну пам’ять, потім виробник почне виробляти предмети. Якщо загальний обсяг виробленого товару дорівнює розміру буфера, виробник чекатиме, поки споживач спожив його. Так само споживач спочатку перевірить наявність товару. Якщо товар відсутній, споживач чекатиме, поки виробник виготовить його. Якщо предмети є в наявності, Споживач їх споживає. Псевдокод для демонстрації наведено нижче:
Спільні дані між двома процесами

C






#define buff_max 25> #define mod %> >struct> item{> >// different member of the produced data> >// or consumed data> >---------> >}> > >// An array is needed for holding the items.> >// This is the shared place which will be> >// access by both process> >// item shared_buff [ buff_max ];> > >// Two variables which will keep track of> >// the indexes of the items produced by producer> >// and consumer The free index points to> >// the next free index. The full index points to> >// the first full index.> >int> free_index = 0;> >int> full_index = 0;> >

>

>

Код процесу виробника

C




item nextProduced;> > >while>(1){> > >// check if there is no space> >// for production.> >// if so keep waiting.> >while>((free_index+1) mod buff_max == full_index);> > >shared_buff[free_index] = nextProduced;> >free_index = (free_index + 1) mod buff_max;> >}>

>

>

Код процесу споживача

C




item nextConsumed;> > >while>(1){> > >// check if there is an available> >// item for consumption.> >// if not keep on waiting for> >// get them produced.> >while>((free_index == full_index);> > >nextConsumed = shared_buff[full_index];> >full_index = (full_index + 1) mod buff_max;> >}>

>

>

У наведеному вище коді Виробник знову почне виробляти, коли (free_index+1) максимальна кількість баффів для модифікації стане безкоштовною, тому що якщо вона не безкоштовна, це означає, що Споживач може споживати предмети, тому немає потреби виробляти більше. Так само, якщо вільний і повний індекси вказують на той самий індекс, це означає, що немає елементів для споживання.

Загальна реалізація C++:

C++




#include> #include> #include> #include> #define buff_max 25> #define mod %> struct> item {> >// different member of the produced data> >// or consumed data> >// ---------> };> // An array is needed for holding the items.> // This is the shared place which will be> // access by both process> // item shared_buff[buff_max];> // Two variables which will keep track of> // the indexes of the items produced by producer> // and consumer The free index points to> // the next free index. The full index points to> // the first full index.> std::atomic<>int>>free_index(0);> std::atomic<>int>>full_index(0);> std::mutex mtx;> void> producer() {> >item new_item;> >while> (>true>) {> >// Produce the item> >// ...> >std::this_thread::sleep_for(std::chrono::milliseconds(100));> >// Add the item to the buffer> >while> (((free_index + 1) mod buff_max) == full_index) {> >// Buffer is full, wait for consumer> >std::this_thread::sleep_for(std::chrono::milliseconds(100));> >}> >mtx.lock();> >// Add the item to the buffer> >// shared_buff[free_index] = new_item;> >free_index = (free_index + 1) mod buff_max;> >mtx.unlock();> >}> }> void> consumer() {> >item consumed_item;> >while> (>true>) {> >while> (free_index == full_index) {> >// Buffer is empty, wait for producer> >std::this_thread::sleep_for(std::chrono::milliseconds(100));> >}> >mtx.lock();> >// Consume the item from the buffer> >// consumed_item = shared_buff[full_index];> >full_index = (full_index + 1) mod buff_max;> >mtx.unlock();> >// Consume the item> >// ...> >std::this_thread::sleep_for(std::chrono::milliseconds(100));> >}> }> int> main() {> >// Create producer and consumer threads> >std::vectorthread>нитки; threads.emplace_back(виробник); threads.emplace_back(споживач); // Зачекайте, поки потоки завершаться для (auto& thread : threads) { thread.join(); } повернути 0; }>

>

>

оператор if-else java

Зауважте, що атомарний клас використовується, щоб переконатися, що спільні змінні free_index і full_index оновлюються атомарно. Мьютекс використовується для захисту критичного розділу, де здійснюється доступ до спільного буфера. Функція sleep_for використовується для імітації виробництва та споживання предметів.

ii) Метод передачі повідомлень

Тепер ми почнемо обговорення зв’язку між процесами через передачу повідомлень. У цьому методі процеси спілкуються один з одним без використання спільної пам’яті. Якщо два процеси p1 і p2 хочуть спілкуватися один з одним, вони виконують наступне:

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

Розмір повідомлення може мати фіксований або змінний розмір. Якщо він має фіксований розмір, це легко для розробника ОС, але складно для програміста, а якщо він має змінний розмір, то це легко для програміста, але складно для розробника ОС. Стандартне повідомлення може складатися з двох частин: заголовок і тіло.
The заголовна частина використовується для зберігання типу повідомлення, ідентифікатора адресата, ідентифікатора джерела, довжини повідомлення та контрольної інформації. Керуюча інформація містить інформацію про те, що робити, якщо закінчилося місце в буфері, порядковий номер, пріоритет. Зазвичай повідомлення надсилається у стилі FIFO.

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

  1. Як встановлюються посилання?
  2. Чи може посилання бути пов’язане з більш ніж двома процесами?
  3. Скільки зв'язків може бути між кожною парою взаємодіючих процесів?
  4. Яка пропускна здатність посилання? Розмір повідомлення, яке може вмістити посилання, є фіксованим чи змінним?
  5. Посилання є однонаправленим чи двонаправленим?

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

Передача повідомлення через обмін повідомленнями.

Синхронна та асинхронна передача повідомлень:
Заблокований процес – це процес, який очікує на певну подію, наприклад, на те, що ресурс стане доступним, або завершення операції вводу/виводу. IPC можливий між процесами на одному комп’ютері, а також між процесами, що виконуються на іншому комп’ютері, тобто в мережевій/розподіленій системі. В обох випадках процес може бути або не бути заблокованим під час надсилання повідомлення або спроби отримати повідомлення, тому передача повідомлення може блокуватися або не блокуватися. Розглядається блокування синхронний і блокування надсилання означає, що відправника буде заблоковано, доки повідомлення не буде отримано одержувачем. Так само блокування отримання має блок одержувача, доки повідомлення не стане доступним. Вважається неблокування асинхронний а неблокуюче надсилання означає, що відправник надсилає повідомлення та продовжує. Подібним чином, неблокуюче отримання дозволяє одержувачу отримати дійсне повідомлення або нуль. Після ретельного аналізу ми можемо прийти до висновку, що для відправника природніше бути неблокованим після проходження повідомлення, оскільки може виникнути необхідність відправити повідомлення іншим процесам. Однак відправник очікує підтвердження від одержувача в разі невдачі надсилання. Подібним чином, більш природним для одержувача є блокування після видачі отримання, оскільки інформація з отриманого повідомлення може бути використана для подальшого виконання. У той же час, якщо відправка повідомлення продовжує не вдаватися, одержувачу доведеться чекати нескінченно довго. Тому ми також розглядаємо іншу можливість передачі повідомлень. В основному є три бажані комбінації:

  • Блокування надсилання та блокування отримання
  • Неблокуюче надсилання та неблокуюче отримання
  • Неблокуюче надсилання та блокування отримання (найчастіше використовується)

У прямій передачі повідомлень , Процес, який хоче спілкуватися, повинен явно вказати одержувача або відправника повідомлення.
напр. відправити (p1, повідомлення) означає відправити повідомлення на p1.
Так само отримати (p2, повідомлення) означає отримати повідомлення від p2.
У цьому методі зв’язку канал зв’язку встановлюється автоматично, який може бути однонаправленим або двонаправленим, але один канал може використовуватися між однією парою відправника та одержувача, і одна пара відправника та одержувача не повинна мати більше однієї пари посилання. Симетрія й асиметрія між надсиланням і отриманням також можуть бути реалізовані, тобто або обидва процеси називатимуть один одного для надсилання та отримання повідомлень, або лише відправник називатиме одержувача для надсилання повідомлення, а одержувачу не потрібно називати імена відправника для отримання повідомлення. Проблема з цим методом зв'язку полягає в тому, що якщо ім'я одного процесу змінюється, цей метод не працюватиме.
У непрямій передачі повідомлень , процеси використовують поштові скриньки (також звані портами) для надсилання та отримання повідомлень. Кожна поштова скринька має унікальний ідентифікатор, і процеси можуть спілкуватися, лише якщо вони спільно використовують поштову скриньку. Посилання встановлюється, лише якщо процеси мають спільну поштову скриньку, а одне посилання може бути пов’язане з багатьма процесами. Кожна пара процесів може спільно використовувати кілька каналів зв'язку, і ці канали можуть бути однонаправленими або двонаправленими. Припустімо, що два процеси хочуть спілкуватися через непряму передачу повідомлень, необхідні операції: створити поштову скриньку, використовувати цю поштову скриньку для надсилання та отримання повідомлень, а потім знищити поштову скриньку. Використовувані стандартні примітиви: відправити повідомлення) що означає відправити повідомлення до поштової скриньки A. Примітив для отримання повідомлення також працює таким же чином, наприклад, отримано (A, повідомлення) . Виникла проблема з реалізацією цієї поштової скриньки. Припустимо, що більше ніж два процеси використовують одну поштову скриньку, і процес p1 надсилає повідомлення до поштової скриньки, який процес буде одержувачем? Цю проблему можна вирішити шляхом примусового використання однієї поштової скриньки лише двома процесами або дозволу лише одному процесу виконувати отримання в певний час або вибору будь-якого процесу випадковим чином і сповіщення відправника про одержувача. Поштову скриньку можна зробити приватною для однієї пари відправника/одержувача, а також можна використовувати спільно для кількох пар відправника/одержувача. Порт — це реалізація такої поштової скриньки, яка може мати кілька відправників і одного отримувача. Він використовується в програмах клієнт/сервер (у цьому випадку сервер є приймачем). Порт належить процесу-одержувачу та створюється ОС за запитом процесу-одержувача та може бути знищений за запитом того самого процесора-одержувача, коли приймач завершує роботу. Змусити лише один процес виконувати прийом можна за допомогою концепції взаємного виключення. Поштова скринька Mutex створюється, який використовується n процесом. Відправник не блокує і надсилає повідомлення. Перший процес, який виконає прийом, увійде в критичний розділ, а всі інші процеси блокуватимуть і чекатимуть.
Тепер давайте обговоримо проблему «виробник-споживач», використовуючи концепцію передачі повідомлень. Виробник розміщує предмети (всередині повідомлень) у поштову скриньку, а споживач може споживати товар, якщо в поштовій скриньці є принаймні одне повідомлення. Код наведено нижче:
Код виробника

C




void> Producer(>void>){> > >int> item;> >Message m;> > >while>(1){> > >receive(Consumer, &m);> >item = produce();> >build_message(&m , item ) ;> >send(Consumer, &m);> >}> >}>

>

>

Кодекс споживача

C




void> Consumer(>void>){> > >int> item;> >Message m;> > >while>(1){> > >receive(Producer, &m);> >item = extracted_item();> >send(Producer, &m);> >consume_item(item);> >}> >}>

>

>

Приклади систем IPC

  1. Posix: використовує метод спільної пам'яті.
  2. Mach: використовує передачу повідомлень
  3. Windows XP : використовує передачу повідомлень за допомогою локальних процедурних викликів

Комунікація в архітектурі клієнт/сервер:
Існують різні механізми:

  • Труба
  • Розетка
  • Віддалені процедурні виклики (RPC)

Вищевказані три методи будуть розглянуті в наступних статтях, оскільки всі вони досить концептуальні і заслуговують на окрему статтю.
Література:

  1. Концепції операційної системи Galvin et al.
  2. Конспекти лекцій/ppt Аріель Дж. Франк, Університет Бар-Ілан

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

Переваги IPC:

  1. Дозволяє процесам спілкуватися один з одним і ділитися ресурсами, що сприяє підвищенню ефективності та гнучкості.
  2. Сприяє координації між декількома процесами, що сприяє кращій загальній продуктивності системи.
  3. Дозволяє створювати розподілені системи, які можуть охоплювати кілька комп’ютерів або мереж.
  4. Може використовуватися для реалізації різних протоколів синхронізації та зв’язку, таких як семафори, канали та сокети.

Недоліки IPC:

  1. Збільшує складність системи, ускладнюючи її проектування, впровадження та налагодження.
  2. Може створювати вразливі місця в безпеці, оскільки процеси можуть мати доступ або змінювати дані, що належать іншим процесам.
  3. Вимагає ретельного керування системними ресурсами, такими як пам’ять і час процесора, щоб гарантувати, що операції IPC не погіршують загальну продуктивність системи.
    Може призвести до неузгодженості даних, якщо кілька процесів намагаються отримати доступ або змінити ті самі дані одночасно.
  4. Загалом, переваги IPC переважують недоліки, оскільки це необхідний механізм для сучасних операційних систем і дозволяє процесам працювати разом і спільно використовувати ресурси гнучким і ефективним способом. Однак необхідно ретельно розробляти та впроваджувати системи IPC, щоб уникнути потенційних вразливостей безпеки та проблем з продуктивністю.

Додаткова довідка:
http://nptel.ac.in/courses/106108101/pdf/Lecture_Notes/Mod%207_LN.pdf
https://www.youtube.com/watch?v=lcRqHwIn5Dk