
Механізми взаємодії між процесами в ос Unix
При вирішенні задачі синхронізації процесів та їх взаємодії за допомогою різних механізмів, що надаються ОС, може знадобитися використання наступних системних викликів
створення, завершення процесу, отримання інформації про процес: fork, exit, getpid, getppid і т. д .;
синхронізація процесів: signal, kill, sleep, alarm, wait, pause, semop, semctl, semcreate і т. д .;
створення інформаційного каналу, розділяється пам'яті, черги повідомлень і робота з ними: pipe, mkfifo, read, write, msgget, shmget, msgctl, shmctl і т. д.
Механізм взаємодії між процесами (Inter-Process Communication Facilities - IPC) включає
засоби, що забезпечують можливість синхронізації процесів при доступі до спільно використовуваних ресурсів - семафори (semaphores);
засоби, що забезпечують можливість посилки процесом безпідставного процесу - черги повідомлень (Message queries);
засоби, що забезпечують можливість наявності загальної для процесів пам'яті - сегменти розділеної пам'яті (shared memory segments);
засоби, що забезпечують можливість «спілкування» процесів, як споріднених, так і ні, через пайпи або канали (pipes).
Найбільш загальним поняттям IPC є ключ, що зберігається в загальносистемній таблиці і означає об'єкт міжпроцесорної взаємодії, доступний декільком процесам. Позначений ключем об'єкт може бути чергою повідомлень, набором семафорів або сегментом розділеної пам'яті. Ключ має тип keyt, склад якого залежить від реалізації і визначається у файлі <sys / types.h>. Ключ використовується для створення об'єкта між процесами взаємодії або отримання доступу до існуючого об'єкта
Іменовані і неіменовані канали (пайпи)
Операційні системи сімейства Unix завжди підтримують два типи одно направлених каналів
• неіменовані канали;
• іменовані канали FIFO.
Неіменовані канали - це найперша форма IPC в Unix (1973), головним недоліком яких є відсутність імені, внаслідок чого вони можуть використовуватися для взаємодії тільки спорідненими процесами. В Unix System третьої редакції (1982) були додані канали FIFO, які називаються іменованими каналами. Абревіатура FIFO розшифровується як «first in, first out» - «першим увійшов, першим вийшов», тобто ці канали працюють як черги. Іменовані канали в Unix функціонують подібно неіменованого - дозволяють передавати дані тільки в одну сторону. Однак на відміну від програмних каналів кожному каналу FIFO зіставляється повне ім'я в файлової системі, що дозволяє двом неспорідненим процесам звернутися до одного і того ж FIFO. Доступ і до іменованих каналах, і до неіменованих організовується за допомогою звичайних функцій read і write.
FIFO створюється викликом mkfifo
#include <sys / types.h> #include <sys / stat.h>
int mkfifo (const char * pathname, mode_t mode);
/ * Повертає 0 при успішному виконанні, -1 при помилці * /
Тут pathname - звичайне для Unix повне ім'я файлу, яке і буде ім'ям FIFO.
Аргумент mode вказує бітову маску дозволів доступу до файлу (табл. 4.2), аналогічно другому аргументу команди open.
Функція mkfifo діє як open, викликана з аргументом mode = O_CREAT | O_EXCL. Це означає, що створюється новий канал FIFO або повертається помилка EEXIST у випадку, якщо канал із заданим повним ім'ям вже існує. Якщо не потрібно створювати новий канал, викликайте open замість mkfifо. Для відкриття існуючого каналу або створення нового, в тому випадку, якщо його ще не існує, викличте mkfifo, перевірте, чи не повернута помилка EEXIST, і якщо таке трапиться, викличте функцію open.
Команда mkfifo також створює канал FIFO. Нею можна користуватися в сценаріях інтерпретатора або з командного рядка.
Живучість каналів визначається живучістю процесів, тобто, канал буде існувати до тих пір, поки він не буде примусово закритий або не залишиться жодного процесу працюючого з каналом.
Після створення канал FIFO повинен бути відкритий на читання або запис за допомогою якої функції open, або однієї зі стандартних функцій відкриття файлів з бібліотеки вводу-виводу (наприклад, fopen). FIFO може бути відкритий або тільки на читання, або тільки на запис. Не можна відкривати канал на читання і запис, оскільки іменовані канали можуть бути тільки односторонніми.
При записи в програмний канал або канал FIFO викликом write дані завжди додаються до вже наявних, а виклик read зчитує дані, поміщені в програмний канал або FIFO першими. При виконанні функції lseek для програмного каналу або FIFO буде повернута помилка ESPIPE.
Неіменовані канали створюються викликом pipe () і надають можливість тільки односпрямованої (односторонньої) передачі даних:
#include <unistd.h>
int fd [2];
pipe (fd);
/ * Повертає 0 у випадку успішного завершення, -1 - у разі помилки; * /
Функція повертає два файлових дескриптора: fd [0] і fd [l], причому перший відкритий для читання, а другий - для запису.
Хоча канал створюється одним процесом, він рідко використовується тільки цим процесом, канали зазвичай використовуються для зв'язку між двома процесами (батьківським і дочірнім) наступним чином: процес створює канал, а потім викликає fork, створюючи свою копію - дочірній процес; потім батьківський процес закриває відкритий для читання кінець каналу, а дочірній - відкритий на запис кінець каналу. Це забезпечує односторонню передачу даних між процесами .
Рис.2. Функціонування каналу для випадку одиночного процесу
Рис.3. Функціонування каналу після створення дочірнього процесу (після виклику fork)
Рис.4. Функціонування каналу між двома процесами
Рис.5. Функціонування каналів між трьома процесами в конвеєрній обробці
При вводі команда типу:
who| sort| 1р
інтерпретатор команд Unix виконує вищеописані дії для створення трьох процесів з двома каналами між ними (рис. 5).
Інтерпретатор також підключає відкритий для читання кінець кожного каналу до стандартного потоку введення, а відкритий на запис до стандартного потоку виводу.
Усі розглянуті вище неіменовані канали були односпрямованим (односторонніми), тобто дозволяли передавати дані тільки в одну сторону. При необхідності передачі даних в обидві сторони потрібно створювати пару каналів і використовувати кожен з них для передачі даних в одну сторону. Етапи створення двонаправленого неіменованого каналу IPC є наступними:
створюються канали 1 (fd1 [0] і fd1 [1]) і 2 (fd2 [0] і fd2 [0]);
виклик fork;
батьківський процес закриває доступний для читання кінець каналу 1 (fd1 [0]);
батьківський процес закриває доступний для запису кінець каналу 2 (fd2 [1]);
дочірній процес закриває доступний для запису кінець каналу 1 (fd1 [1]);
дочірній процес закриває доступний для читання кінець каналу 2 (fd2 [0]).