МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ
НАЦІОНАЛЬНИЙ УНІВЕРСИТЕТ «ЛЬВІВСЬКА ПОЛІТЕХНІКА»
ІКТА
кафедра ЗІ
ЗВІТ
до лабораторної роботи №4
з курсу:
«Операційні системи»
на тему:
«Передача даних між процесами в ОС Linux»
Варіант 13(4)
Львів-2015
Мета : Оволодіти практичними навичками роботи з процесами в ОС Linux. Ознайомитись із механізмами взаємодії між процесами (Inter-Process Communication Facilities - IPC) в Linux. Навчитись створювати та використовувати іменовані та не іменовані канали, черги повідомлень та сегменти розподіленої пам’яті.
Теоретичні відомості
Міжпроцесна взаємодія
Для підвищення ефективності функціонування обчислювальної системи використовують два види взаємодії процесів
• псевдопаралельне (виконання на одній обчислювальній системі);
• паралельне (виконання на різних обчислювальних системах).
Існують різні причини кооперації процесів :
підвищення швидкості роботи (один процес в очікуванні, інший виконує корисну роботу, спрямовану на вирішення загального завдання);
спільне використання даних (використання різними процесами однієї і тієї ж динамічної бази даних або файлу);
модульна конструкція якої-небудь системи (наприклад, мікроядерний спосіб побудови ОС, коли взаємодія процесів здійснюється шляхом передачі повідомлень через мікроядро);
для зручності роботи користувача (наприклад, при одночасному редагуванні і відлагодженні програми, процеси редактора і відладчика повинні взаємодіяти).
Розрізняють два види процесів:
• кооперативні (впливають на взаємну поведінку шляхом обміну інформацією);
• незалежні (діяльність процесів залишається незмінною при будь прийнятої інформації).
За обсягом переданої інформації та ступеня можливого впливу на поведінку іншого процесу всі засоби такого обміну можна розділити на три категорії
1) сигнальні;
2) канальні;
3) розділена пам'ять.
У разі сигнального обміну передається мінімальна кількість інформації, достатня для сповіщення процесу про настання події.
При канальному обміні інформацією «спілкування» процесів відбувається через лінії зв'язку, надані ОС. Обсяг переданої інформації в цьому випадку в одиницю часу обмежений пропускною здатністю ліній зв'язку.
При використанні процесами розділеної пам'яті спільно використовується деяка область адресного простору, сформована ОС. Цей спосіб обміну інформацією являє собою найбільш швидкий спосіб взаємодії процесів в одній обчислювальній системі, але вимагає при використанні підвищеної уважності.
Розрізняють два способи адресації при обміні інформацією між процесами
• прямий - процеси здійснюють операції обміну даними явно вказуючи ім'я або номер цих процесів;
непрямий - дані поміщаються передаючим процесом в певний проміжний об'єкт для зберігання даних з адресою, звідки вони потім можуть бути вилучені яким-небудь іншим процесом.
Пряма адресація може бути двох типів
• симетрична - процеси, які приймають і передають дані, вказують імена своїх партнерів по взаємодії, при цьому жоден інший процес не може втрутитися в процедуру симетричного прямого спілкування двох процесів, перехопити послані або підмінити очікувані дані;
• асиметрична - тільки один з взаємодіючих процесів вказує ім'я свого партнера по кооперації, а другий процес як можливого партнера розглядає будь-який процес в системі.
Слід виділити дві різні моделі передачі даних по каналах зв'язку
• З використанням потоку вводу-виводу. Не важлива структура даних, не здійснюється їх інтерпретація; процес, який прочитав 100 байт з лінії зв'язку, не знає, чи були вони передані одночасно, прийшли від одного процесу або від різних. Прикладом такої моделі є «pipe» (пайп або канал);
• За допомогою повідомлень. На передані дані накладається певна структура, весь потік інформації розділяється на окремі повідомлення, вводячи між даними, принаймні, кордони повідомлень.
Найбільш простий варіант пайпа (каналу) - неіменованого канал створює оболонка Unix (наприклад, bash) між програмами, що запускаються з командного рядка, розділеними символом «|».
Наприклад, командний рядок
dmesg | less
створює канал від програми dmesg до less, що виводить налагоджувальні повідомлення ядра, до програми посторінкового перегляду less.
Механізми взаємодії між процесами в ОС 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. Нею можна користуватися в сценаріях інтерпретатора або з командного рядка.
Черги повідомлень
Для забезпечення можливості обміну повідомленнями між процесами механізм черг підтримується наступними системними викликами.
msgget для утворення нової черги повідомлень або отримання дескриптора існуючої черги;
msgsnd для постановки повідомлення в зазначену чергу повідомлень;
msgrcv для вибірки повідомлення з черги повідомлень;
msgctl для виконання ряду керуючих дій.
Прототипи перерахованих системних викликів описані в файлах
#include <sys / ipc.h>
#include <sys / msg.h>
По системному виклику msgget у відповідь на ключ (key), що визначає унікальне ім'я
черги, і набір прапорів (повністю аналогічні прапорам в системному виклику semget). Викликом msgget ядро, або створює нову чергу повідомлень в ядрі і повертає користувачу ідентифікатор створеної черги, або знаходить елемент таблиці черг повідомлень ядра, що містить зазначений ключ, і повертає відповідний ідентифікатор черги.
int msgqid = msgget(key_t key, int flag)
Таким чином, черга повідомлення володіє живучістю ядра. Для поміщення повідомлення в чергу служить системний виклик msgsnd.
int rnsgsnd (int msgqid, void *rnsg, size_t size, int flaq)
де msg - це покажчик на структуру довжиною size, що містить визначений користувачем цілочисельний тип повідомлення і символьний масив-повідомлення, причому розмір користувальницьких даних обчислюється таким чином: size = sizeof (msg) - sizeof (long).
Структура msg завжди має вигляд
struct rnsq {
lonq mtype; / * Тип повідомлення * /
char mtext [SOMEVALUE]; / * текст повідомлення * /
};
Поле типу long завжди повинно бути першим у структурі, далі можуть слідувати в довільному порядку дані користувача, в цьому випадку ядро не накладає обмеження на тип даних, а тільки на їх довжину (залежну від реалізації системи). Параметр flag визначає дії ядра для потоку, що здійснив виклик при читанні черги чи виходу за межі допустимих розмірів внутрішньої буферної пам'яті.
Робота з розподіленою пам'яттю
Для роботи з пам'яттю, що розділяється використовуються системні виклики:
shmget створює новий сегмент розподіленої пам'яті або знаходить існуючий сегмент з тим самим ключем;
shmat підключає сегмент із зазначеним описувачем до віртуальної пам'яті процесу, що здійснює звернення;
shmdt відключає від віртуальної пам'яті раніше підключений до неї сегмент із зазначеною віртуальною адресою початку;
shmctl служить для управління різноманітними параметрами, пов'язаними з існуючим сегментом.
Прототипи перерахованих системних викликів описані в файлах
#include <sys / ipc.h>
#include <sys / shm.h>
Після того як сегмент розподіленої пам'яті підключений до віртуальної пам'яті процесу, цей процес може звертатися до відповідних елементів пам'яті з використанням звичайних машинних команд читання і запису.
Системний виклик
int shmid = shmget (key_t key, size_t size, int flag)
на підставі параметру size визначає бажаний розмір сегменту в байтах. Якщо в таблиці розподіленої пам'яті знаходиться елемент, що містить заданий ключ, і права доступу не суперечать поточним характеристикам процесу, що здійснює звернення, то значенням системного виклику є ідентифікатор існуючого сегменту, причому параметр size повинен бути в цьому випадку рівним 0. В іншому випадку створюється новий сегмент з розміром не менше встановленого в системі мінімального розміру сегменту розподіленої пам'яті і не більше встановленого максимального розміру. Живучість об'єктів розподіленої пам'яті визначається живучістю ядра. Створення сегменту не означає негайного виділення під нього основної пам'яті, і ця дія відкладається до виконання першого системного виклику підключення сегменту до віртуальної пам'яті деякого процесу. Прапори IPCCREAT і IPCEXCL аналогічні розглянутим вище.
Підключення сегменту до віртуальної пам'яті виконується шляхом звернення до системного виклику shmat
void * virtaddr = shmat (intshmid, void * daddr, int flags)
Параметр shmid – це раніше отриманий ідентифікатор сегменту, a daddr – бажана процесом віртуальна адреса, яка повинна відповідати початку сегменту в віртуальній пам'яті.
Значенням системного виклику є фактична віртуальна адреса початку сегменту. Якщо значенням daddr є NULL, ядро вибирає найбільш зручну віртуальну адресу початку сегмента. Прапори системного виклику shmat наведені нижче в таблиці.
Для відключення сегменту від віртуальної пам'яті використовується системний виклик shmdt:
int shmdt (* daddr)
де daddr – це віртуальна адреса початку сегменту у віртуальній пам'яті, раніше отриманий від системного виклику shmat.
Системний виклик shmctl
int shmctl (int shmid, int command, struct shmid_ds * shrn_stat)
по синтаксису і призначенню аналогічний msgctl.
Завдання
Створити 2 програми на мові С (процеси). За допомогою сегменту розподіленої пам’яті забезпечити передачу інформації від одного процесу до іншого. В якості інформації, що передається використати повідомлення введене з клавіатури.
Текст програми
Файл in.c
#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <error.h>
#include <fcntl.h>
#define SVSHM_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
#define STRING_SIZE 500
int main(int argc, char **argv)
{
if (argc != 2)
{
printf("usage: shmwrite <path_to_file>\n");
return 0;
}
printf ("type the message:\n");
char s[STRING_SIZE];
size_t length = 0;
int i = 0;
char c;
while (((c = getchar()) != '\n')&&(i < STRING_SIZE - 1))
{
s[i++] = c;
length++;
}
s[i] ='\0';
int oflag = SVSHM_MODE|IPC_CREAT;
int id = shmget(ftok(argv[1], 0), length, oflag);
unsigned char *ptr = (unsigned char*) shmat(id, NULL, 0);
struct shmid_ds buff;
shmctl(id, IPC_STAT, &buff);
for (i = 0; i < length; i++)
*ptr++ = s[i];
return 0;
}
Файл out.c
#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <error.h>
#include <fcntl.h>
#define SVSHM_MODE ( S_IRUSR| S_IWUSR | S_IRGRP | S_IROTH )
int main(int argc, char **argv)
{
if (argc != 2)
{
printf("usage: shmwrite <path_to_file>\n");
return 0;
}
int id = shmget(ftok(argv[1], 0), 0, SVSHM_MODE);
unsigned char *ptr = (unsigned char*) shmat(id, NULL, 0);
struct shmid_ds buff;
shmctl(id, IPC_STAT, &buff);
int i;
for (i = 0; i < buff.shm_segsz; i++)
printf ("%c",(unsigned char) *ptr++);
printf("\n");
shmctl(id, IPC_RMID, NULL);
return 0;
}
Результат виконання
/
Висновок
У даній роботі я оволодів практичними навичками роботи з процесами в ОС Linux та механізмами взаємодії між процесами (Inter-Process Communication Facilities - IPC) в Linux. Навчився створювати та використовувати сегменти розподіленої пам’яті.