МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ
НАЦІОНАЛЬНИЙ УНІВЕРСИТЕТ «ЛЬВІВСЬКА ПОЛІТЕХНІКА»
Кафедра ЕОМ
Лабораторна робота №2
Здисципліни: «Організація обчислювальних процесів в паралельних системах»
На тему: «Обмін даними в ОС UNIX за допомогою неіменованих та іменованих каналів.»
Львів-2014
Мета роботи : Засвоїти принципи роботи неіменованих та іменованих каналів.
Завдання :
Неіменовані канали
1) Написати програму, що породжує синівський процес, який обмінюється інформацією з батьківським. При цьому синівський процес перенаправляє стандартний потік виводу у неіменований канал і передає інформацію батьківському використовуючи функцію printf(). Батьківський процес перенаправляє стандартний потік вводу і отримує інформацію від синівського використовуючи функцію gets().1.2.
Іменовані канали
2) Організувати обмін інформацією між трьома програмами (що працюють одночасно). Дві з них пишуть інформацію в іменований канал, третя зчитує її (при цьому всі повідомлення від першої програми друкуються на екрані, повідомлення від другої програми ігноруються і виводиться інформація про кількість проігнорованих повідомлень).
Теоретичні відомості
Інформація про файли, які використовує процес, входить до складу його системного контексту і зберігається в його блоці керування - PCB. В операційній системі UNІX можна спрощено вважати, що інформація про файли, з якими процес здійснює операції потокового обміну, разом з інформацією про потокові лінії в'язку, що з'єднують процес з іншими процесами і пристроями вводу-виводу, зберігається в деякому масиві, що одержав назву таблиці відкритих файлів (таблиці файлових дескрипторів).Індекс елемента цього масиву, що відповідає визначеному потоку вводу-виводу, одержав назву файлового дескриптора для цього потоку. Таким чином, файловий дескриптор являє собою не велике ціле число, що для поточного процесу в даний момент часу однозначно визначає деякий діючий канал вводу-виводу. Деякі файловідескриптори на етапі старту будь-якої програми асоціюються зі стандартними потоками вводу-виводу. Так, наприклад, файловий дескриптор 0 відповідає стандартному потоку вводу, файловий дескриптор 1 - стандартному потоку виводу, файловий дескриптор 2 - стандартному потоку для виводу помилок. У нормальному інтерактивному режимі роботи стандартний потік вводу зв'язує процесс із клавіатурою, а стандартні потоки виводу і виводу помилок - з поточним терміналом.
Файловий дескриптор використовується як параметр, що описує потік вводу-виводу, для системних викликів, що виконують операції над цим потоком. Тому перш ніж робити операції читання даних з файлу і запису їх у файл, ми повинні помістити інформацію про файл у таблицю відкритих файлів і визначити відповідний файловий дескриптор. Для цього застосовується процедура відкриття файлу, здійснювана системним викликом open().
Після завершення потокових операцій процес повинен виконати операцію закриття потоку вводу-виводу, під час якої відбудеться остаточне скидання буферів на лінії зв'язку, звільняться виділені ресурси операційної системи. При цьому елемент таблиці відкритих файлів, що відповідає файловому дескриптору, буде позначений як вільний. За ці дії відповідає системний виклик close(). Треба відзначити, що при завершенні роботипроцесу за допомогою явного чи неявного виклику функці їexіt() відбувається автоматичнее закриття усіх відкритих потоків вводу-виводу.
Найбільш простим способом для передачі інформації за допомогою потокової моделі між різними процессами чи навіть всередині одного процесу в операційній системі UNIX є pіpe (канал, труба, конвеєр).
Важлива відмінність pіp'а від файлу полягає в тім, що прочитана інформація негайно видаляється з нього і не може бути прочитана повторно.
Системний виклик open призначений для виконання операції відкриття файлу і, у випадку його вдалого здійснення, повертає файловий дескриптор відкритого файлу.
Системні виклики dup і dup2 створюють копію файлового дескриптора.
Системний виклик wrіte призначений для здійснення потокових операцій виводу (запису) інформації над каналами зв'язку, що описуються файловими дескрипторами, тобто для файлів, pіpe, FІFO і socket.
Системний виклик read призначений для здійснення потокових операцій вводу (читання) інформації над каналами зв'язку, що описуються файловими дескрипторами, тобто для файлів, pіpe, FІFO іsocket.
Для організації потокової взаємодії процесів (які не обов’язково мають загального батька) в операційній системі UNІX застосовується засіб зв'язку, що одержав назву FІFO (від Fіrst Іnput Fіrst Output) чи іменований pіpe. FІFO в усьому подібний pіp'у, за одним винятком: дані про розташування FІFO в адресному просторі ядра і його стан процеси можуть одержувати не через “родинні” зв'язки, а через файлову систему. Для цього при створенні іменованого pіp'а на диску створюється файл спеціального типу, звертаючись до якого процеси можуть одержати потрібну їм інформацію. Для створення FІFO використовується системний виклик mknod() чи існуюча в деяких версіях UNІX функція mkfіfo().
При їхній роботі не відбувається дійсного виділення області адресного простору операційної системи під іменований pіpe, а тільки заводиться файл-мітка, існування яко їдозволяє здійснити реальну організацію FІFO у пам'яті при йоговідкритті за допомогою системного виклику open().
Системний виклик mkfifo створює новий спеціальний FIFO файл (іменований канал) із маршрутним ім'ям, на яке вказує аргумент path. Права доступу до нього визначається аргументом mode.
Лістинг програми :
Lab1.c
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#define SLEEP_TIME 3
#define ITERATION_NUM 5
#define BUF_SIZE 50
void main(void)
{
inti;
intmPid;
intcPid;
intiNewPipeHandle;
charbuf[BUF_SIZE];
intlocpip[2];
int status;
interrFlag = pipe(locpip);
if(errFlag ==-1)
{
printf("Cant create the PIPE.\n");
exit(1);
}
printf("Parent: pipe read ID = %d, pipe write ID = %d.\n", locpip[0], locpip[1]);
cPid = fork();
switch(cPid)
{
case -1:
fprintf(stderr,"Can't fork for the child.\n");
exit(1);
case 0:
close(locpip[0]);
mPid = getpid();
printf("Child: My PID = %d.\n", mPid);
iNewPipeHandle=dup2(locpip[1],STDOUT_FILENO);
fprintf(stderr,"Child: outpipe = %d \n",iNewPipeHandle);
for(i = 0; i< ITERATION_NUM; i++)
{
sprintf(buf,"Data send from child: %s %d\n", "Hello",i);
printf (buf);
sleep (SLEEP_TIME);
}
close(locpip[1]);
exit(0);
default:
close(locpip[1]);
mPid = getpid();
printf("Parent: My PID = %d.\n", mPid);
iNewPipeHandle=dup2(locpip[0],STDIN_FILENO);
printf("Parent: inpipe = %d \n",iNewPipeHandle);
for(i=0; i< ITERATION_NUM; i++)
{
gets(buf);
printf("Data received by parent from child is: %s\n",buf);
sleep(SLEEP_TIME);
}
waitpid (cPid,&status,0);
close(locpip[0]);
exit(0);
}
}
Результати виконання:
Parent: pipe read ID = 3, pipe write ID = 5.
Child: My PID = 4909.
Child: outpipe = 1
Parent: My PID = 4908.
Parent: inpipe = 0
Data received by parent from child is: Data send from child: Hello 0
Data received by parent from child is: Data send from child: Hello 1
Data received by parent from child is: Data send from child: Hello 2
Data received by parent from child is: Data send from child: Hello 3
Data received by parent from child is: Data send from child: Hello 4
Lab2.c
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <stdio.h>
#define SLEEP_TIME 2
#define ITERATION_NUM 8
#define BUF_SIZE 50
#define FIFOPIPE "fifopipe"
// PERM = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH
#define PERM 0666
void main(void)
{
inti;
intccPid;
intn_ignored;
intfifoPipe;
charbuf[BUF_SIZE];
interrFlag;
errFlag = mknod(FIFOPIPE, S_IFIFO|PERM, 0);
if(errFlag == -1)
{
fprintf(stderr, "Can't create the named fifo pipe.\n");
exit(1);
}
printf("Parent: named fifo pipe has been created '%s'.\n", FIFOPIPE);
intmPid;
intcPid = fork();
switch(cPid)
{
case -1:
fprintf(stderr, "Can't fork for the child.\n");
exit(1);
case 0:
ccPid = fork();
switch(ccPid)
{
case -1:
fprintf(stderr, "Child: Can't fork for the child.\n");
exit(1);
case 0:
mPid = getpid();
printf("Childs child: My PID = %d.\n", mPid);
fifoPipe = open(FIFOPIPE, O_WRONLY);
if(fifoPipe == -1)
{
fprintf(stderr, "Childs child: Can't open the named fifo pipe to write.\n");
exit(1);
}
printf("Childs child: named fifo pipe write ID=%d.\n", fifoPipe);
for(i = 0; i< ITERATION_NUM; i++)
{
sprintf(buf, "cc:Transaction message number %05d.", i);
write(fifoPipe, buf, BUF_SIZE);
printf("Childs child to top parent: %s\n", buf);
//for(j = 0; j < 1000000; j++);
sleep(SLEEP_TIME);
}
close(fifoPipe);
exit(0);
default:
mPid = getpid();
printf("Child: My PID=%d.\n", mPid);
fifoPipe = open(FIFOPIPE, O_WRONLY);
if(fifoPipe == -1)
{
fprintf(stderr, "Can't open the named fifo pipe to write.\n");
exit(1);
}
printf("Child: named fifo pipe write ID=%d.\n", fifoPipe);
for(i = 0; i< ITERATION_NUM; i++)
{
sprintf(buf, "Transaction message number %05d.", i);
printf("Child to Parent: %s\n", buf);
write(fifoPipe, buf, BUF_SIZE);
//for(j=0;j<1000000;j++);
sleep(SLEEP_TIME);
}
close(fifoPipe);
exit(0);
}
default:
mPid = getpid();
printf("Parent: My PID=%d.\n", mPid);
fifoPipe = open(FIFOPIPE, O_RDONLY);
if(fifoPipe == -1)
{
fprintf(stderr, "Can't open the named fifo pipe to read.\n");
exit(1);
}
printf("Parent: named fifo pipe read ID=%d.\n", fifoPipe);
n_ignored=0;
for(i = 0; i< ITERATION_NUM*2; i++)
{
read(fifoPipe, buf, BUF_SIZE);
if (buf[1]!='c')
printf("Parent from child: %s\n", buf);
else
++n_ignored;
//for(j=0;j<1000000;j++);
sleep(SLEEP_TIME);
}
printf("Parent: Number of ignored messages is: %d\n",n_ignored);
int status;
wait(&status);
close(fifoPipe);
errFlag = unlink(FIFOPIPE);
if(errFlag == -1)
{
fprintf(stderr, "Can't delete the named fifo pipe.\n");
exit(1);
}
printf("Parent: named fifo pipe has been deleted '%s'.\n", FIFOPIPE);
exit(0);
}
}
Результати виконання:
Parent: named fifo pipe has been created 'fifopipe'.
Childs child: My PID = 15162.
Child: My PID=15161.
Parent: My PID=15160.
Parent: named fifo pipe read ID=3.
Child: named fifo pipe write ID=3.
Child to Parent: Transaction message number 00000.
Childs child: named fifo pipe write ID=3.
Childs child to top parent: cc:Transaction message number 00000.
Parent from child: Transaction message number 00000.
Child to Parent: Transaction message number 00001.
Childs child to top parent: cc:Transaction message number 00001.
Child to Parent: Transaction message number 00002.
Childs child to top parent: cc:Transaction message number 00002.
Parent from child: Transaction message number 00001.
Child to Parent: Transaction message number 00003.
Childs child to top parent: cc:Transaction message number 00003.
Child to Parent: Transaction message number 00004.
Childs child to top parent: cc:Transaction message number 00004.
Child to Parent: Transaction message number 00005.
Childs child to top parent: cc:Transaction message number 00005.
Parent from child: Transaction message number 00002.
Child to Parent: Transaction message number 00006.
Childs child to top parent: cc:Transaction message number 00006.
Parent from child: Transaction message number 00003.
Child to Parent: Transaction message number 00007.
Childs child to top parent: cc:Transaction message number 00007.
Parent from child: Transaction message number 00004.
Parent from child: Transaction message number 00005.
Parent from child: Transaction message number 00006.
Parent from child: Transaction message number 00007.
Parent: Number of ignored messages is: 8
Parent: named fifo pipe has been deleted 'fifopipe'.
Висновок: при виконанні даної лабораторної роботи я ознайомився з механізмом неіменованих каналів (pipe) в ОС UNIX, та навчився передавати інформацію між паралельними процесами за допомогою механізму pipe, а також ознайомився з механізмом іменованих каналів (pipe) в ОС UNIX, та навчився передавати інформацію між паралельними процесами за допомогою системного виклику mkfifo.