Приклад. 1. Очікувати завершення нащадка pid і отримати код завершення

1. Очікувати завершення нащадка pid і отримати код завершення.

Ec_neg1 (waitpid (pid, & status, 0))

 

2. Чекати завершення будь-якого з нащадків, без отримання коду завершення. * /

Ec_neg1 (pid = waitpid (-1, NULL, 0))

 

3. Отримати від будь-якого з нащадків по груповому ідентифікатору pgid повідомлення про завершення або про припинення і отримати код стану. Не чекати, якщо нащадок ще не змінив стан. * /

Ec_neg1 (pid = waitpid (-pgid, & status, WNOHANG | WUNTRACED))

 

 

Другий представник групи системних викликів wait представляє собою спроще щенний варіант waitpid і відповідає виклику останнього з аргументом pid, рав ним -1, і з аргументом options рівним нулю:

 

wait - очікує завершення дочірнього процесу

# include <sys/wait.h>

 

pid_t wait (int * statusp,

statusp - покажчик на статус або NULL;

Повертає ідентифікатор процесу або -1 у випадку помилки (код помилки-у змінній errno).

 

Системний виклик wait досить рідко використовується в великих програмах, по скільки очікує завершення будь-якого нащадка. Справа в тому, що коли деяка функція, яка є частиною великого програми, створює дочірній процес і намагається його почекати, вона може випадково «дочекатися» завершення зовсім іншого нащадка, вносячи безлад і сум'яття у хід виконання всього програми в цілому. Для таких випадків waitpid підходить набагато краще, так як він дозволяє очікувати конкретний процес або, no принаймні, члена групи процесів. Ніколи не використовуйте wait при написанні бібліотечних функцій, які створюють дочірні процеси.

Припустимо, що процес має двох нащадків і заздалегідь не відомо, який із них завершить роботу першим. Якщо нам потрібно тільки дочекатися завершення обох, можна спочатку почекати завершення будь-якого з нащадків, а потім викликати waitpid для очікування завершення іншого. Або батьківський процес може виконувати будь-яку роботу і періодично викликати waitpid з прапором WNOHANG для кожного з нащадків. Але, як ми вже говорили, ніколи не використовуйте waitpid c аргументом pid, рівним -1, якщо додатком не гарантується відсутність інших нащадків. Взагалі, у великих і складних проектах, де над окремими частинами працюють цілі групи розробників (наприклад, бібліотеки для роботи із зображеннями або бібліотеки для взаємодії з базами даних) таких гарантій дати ніхто не може. Було б просто чудово, якби існувала така різно видність системного виклику wait, якому можна було б передати цілий масив ідентифікаторів, але, на жаль, такого виклику немає.

 

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

 

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <errno.h>

int main()

{

int x, pid;

x=2;

printf("Single process, x=%d\n",x);

pid=fork();

if(pid == 0)

printf("New, x=%d\n",x); // Нащадок

else if(pid > 0)

{ // Батько

printf("Old, pid=%d, x=%d\n",pid,x);

sleep(5);

wait(pid);

}

else

{ perror("Fork error ");

return -1;

}

return 0;

}

 

Наступний приклад показує, як можна за допомогою виклику exec у знову створеному дочірньому процесі виконувати іншу програму, не стираючи з пам'яті батьківський процес.

 

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <errno.h>

main()

{

pid_t pid;

switch (pid = fork())

{

case -1:

printf(“Помилка виклику fork”);

break;

case 0: /* Нащадок викликає exec */

execl (“/bin/ls”, “ls”, “-l”, (char *)0);

printf(“Помилка виклику exec”);

break;

default: /* Батьківський процес викликає wait для припинення */

/* роботи до завершення дочірнього процесу. */

wait ( (int *)0);

printf (“ Програма ls завершилася\n”);

exit (0);

}

}

7. Індівідуальні завдання

1. Призупинити на 1 з батьківський процес. У дочірньому процесі за допомогою системного виклику system() виконати стандартну команду ps, перенаправивши вивод у файл номер 1. Слідом за цим завершити дочірній процес. У батьківському процесі викликати ps і перенаправляти у файл номер 2. Звільнити осередок таблиці процесів породженого процесу.

2. Призупинити на 1 з батьківський процес. Виконати в дочірньому процесі один із системних викликів exec(), передавши йому як параметр стандартну програму ps. Аналогічно виконати виклик ps у батьківському процесі. Результати роботи команд ps в обох процесах перенаправляти в той самий файл.

3. Визначити в програмі глобальну змінну var зі значенням, рівним 1. Перевизначити стандартний висновок і батьківського, і дочірнього процесів у той самий файл. До виконання розгалуження збільшити на 1 змінну var, причому вивести її значення, як до збільшення, так і після. У батьківському процесі збільшити значення змінної на 3, а в дочірньому на 5. Вивести значення змінної до збільшення й після нього усередині кожного із процесів. Результат пояснити.

4. Призупинити на 1 з дочірній процес. У дочірньому процесі за допомогою системного виклику system() виконати стандартну команду ps, перенаправивши вивод у файл номер 1. Слідом за цим завершити дочірній процес. У батьківському процесі викликати ps і перенаправляти у файл номер 2. Звільнити осередок таблиці процесів породженого процесу.

5. Призупинити на 1 з дочірній процес. Виконати в дочірньому процесі один із системних викликів exec(), передавши йому як параметр стандартну програму ps. Аналогічно виконати виклик ps у батьківському процесі. Результати роботи команд ps в обох процесах перенаправляти в той самий файл. Звільнити осередок таблиці процесів породженого процесу.

6. Програма породжує через кожні 2 секунди 5 нових процесів. Кожний із цих процесів виконується заданий час і зупиняється, сповіщаючи про це батькові. Програма-Батько виводить на екран всі повідомлення про зміни в процесах.

7. Програма запускає за допомогою функції exec() новий процес. Завершити процес-нащадок раніше формування батьком виклику. Повторити запуск програми за умови, що процес нащадок завершується після формування виклику wait(). Проаналізувати результати.

Лабораторна робота №9. Використання каналів

Мета:Оволодіння технологією використання каналів ОС UNIX. Застосування отриманих знань для написання програм керування файлами.

 

Завдання для самостійної підготовки:

Вивчити:

− способи межпроцессного взаємодії ОС UNIX;

− організацію каналів ОС UNIX;

− системні виклики керування каналами;

 

Методичні вказівки

1. Заганьні відомості

Система Linux IPC (Inter-process communication) надає кошти для взаємодії процесів між собою.

У розпорядженні програмістів є кілька методів IPC:

− напівдуплексні канали UNIX,

− FIFO (іменовані канали),

− черги повідомлень,

− безлічі семафорів,

− поділювані сегменти пам'яті,

− мережні сокети,

− полнодуплексні канали

 

2. Використання каналів

 

Канал - це засіб зв'язку стандартного висновку одного процесу зі стандартним уведенням іншого. Канали - це найстарший з інструментів IPC, що існує приблизно із часу появи самих ранніх версій оперативної системи UNIX. Вони надають метод однобічних комунікацій (звідси термін half-duplex) між процесами.

Коли процес створює канал, ядро встановлює два файлових дескриптори для користування цим каналом. Один такий дескриптор використовується, щоб відкрити шлях уведення в канал (запис), у той час як іншої застосовується для одержання даних з каналу (читання). Якщо процес посилає дані через канал (fd0), він має можливість одержати цю інформацію з fd1. процес, що створює канал, звичайно породжує дочірній процес. Як тільки дочірній процес успадкує який-небудь відкритий файловий дескриптор від батька, ми одержуємо базу для мультипроцессовой комунікації (між батьком і нащадком). Конструкція каналу виглядає в такий спосіб

 

in <----- in

Parent Process Kernel Child Process

out <----- out

 

Рисунок 4 – Конструкція каналу

 

Створення каналів виконується з використанням функції

#include <unistd.h> int pipe(int *filedes);

 

Функція повертає два дескриптори:

filedes[0] - для запису;

filedes[1] - для читання.

 

Обмін інформацією виконується з використання функцій запису й читання API. Канали використовуються для родинних процесів.

Незалежні процеси можуть використовувати іменовані канали. Такі канали створюються функцією.

 

#include <sys/types.h>

#include <sys/stat.h>

#include <sys/fcntl.h>

#include <unistd.h>

int mknod(const char *pathname, mode_t, dev_t dev);

 

Перший параметр специфицирует ім'я створюваного каналу, параметр mode задає права доступу й тип (для іменованого каналу використовується значення S_IFIFO). Третій параметр ігнорується. Функція повертає ознаку нормального завершення - 0, при помилці вертається значення -1.

Знищення каналу виконується по функції

int unlink(const char *pathname)

 

Наступний приклад ілюструє передачу короткого повідомлення між родительски й дочірнім процесом.

 

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <errno.h>

#include <sys/types.h>

int main() {

pid_t childPid;

int flds[2], status;

char buf[]="Message";

// Створення каналу

if (pipe(flds) == -1)

{ perror("Pipe"); exit(1); }

// Розгалуження процесів

switch (childPid=fork())

{ case -1: perror("fork");

exit(2);

case 0:

close(flds[0]);

//Нащадок

printf("Child process %d\n", getpid()); write(flds[1], buf,

strlen(buf));

close(flds[1]); exit(0); }

// Процес - батько

printf("Process %d\n", getpid());

close(flds[1]);

read(flds[0], buf, 80);

printf("String -> %s\n", buf);

close(flds[0]);

wait(&status);

return status;

}

 

На початку програми створюється канал і формуються два ідентифікатори файлів для цього каналу (flds[0] і flds[1]). Будемо вважати, що в батьківського процесу flds[0] використовується для прийому даних, тому на початку секції батьківського процесу необхідно закрити канал, пов'язаний з файловим ідентифікатором flds[1]. У породженому процесі закривається канал з ідентифікатором flds[0].

Наступний приклад ілюструє обмін даними між двома незалежними процесами через іменований канал.

Перша програма служить сервером, вона створює іменований канал по функції

mkfifo(NAME, S_IFIFO|S_IRWXU|S_IRWXG|S_IRWXO).

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

 

/* Сервер. Створює FIFO і очікує повідомлення */

#include <iostream.h>

#include <stdio.h>

#include <errno.h>

#include <sys/types.h>

#include <unistd.h>

#include <fcntl.h>

#define NAME "sfifo.cc"

int main() {

int fd;

char buf[80];

unlink(NAME);

if(mkfifo(NAME, S_IFIFO|S_IRWXU|S_IRWXG|S_IRWXO)) {

perror("Помилка FIFO");

return 1; }

if((fd=open(NAME, O_RDONLY))==-1) {

perror("Помилка відкриття файлу сервера"); }

read(fd, buf, sizeof(buf));

cout<<"Отримано->"<<buf<<endl; close(fd);

unlink(NAME);

return 0;

}

Програма - клієнт виводить на екран текст запиту на уведення повідомлення й після уведення рядка відкриває канал на запис. Після передачі вмісту буфера в канал, останній закривається.

 

/* Клієнт */

#include <iostream.h>

#include <stdio.h>

#include <errno.h>

#include <sys/types.h>

#include <unistd.h>

#include <fcntl.h>

#include <string.h>

#include <sys/stat.h>

#define NAME "sfifo.cc"

int main() {

char text[80];

int fd;

cout<<"Увести ссобщение"<<endl;

cin>>text;

if((fd=open(NAME, O_WRONLY))==-1) {

perror("Помилка відкриття клієнта");

return 1; }

write(fd, text, strlen(text)); close(fd); return 0;

}

3. Індівідуальні завдання

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

Варіанти завдань

1. Клієнт передає серверу через канал запит у вигляді повного шляху до файлу. Сервер читає цей файл і передає клієнтові його вміст або повідомлення про помилку, якщо файл із зазначеним ім'ям не існує або не доступний для читання. Клієнт виводить прийняті дані на термінал.

2. Клієнт і сервер обмінюються повідомленнями, що вводяться із клавіатури. Програми запускаються на різних терміналах. Прийняті повідомлення виводяться на екран.

3. Сервер виконує команду ps, і результати її виконання передаються клієнтові, що виводить їх на термінал.

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

5. Клієнт передає серверу запит у вигляді повного шляху до файлу. Сервер читає цей файл і передає клієнтові його вміст або повідомлення про помилку, якщо файл не існує або не доступний для читання. Клієнт записує отриману інформацію у файл у поточному каталозі з тим же ім'ям і доповнює його розширенням result.

6. Клієнт приймає із клавіатури команди й передає їхньому серверу. Сервер виконує прийняті команди й повертає результати їхнього виконання клієнтові. Прийняті дані клієнт виводить на термінал. Програми запускати на різних терміналах.

7. Клієнт приймає із клавіатури команди й передає їхньому серверу. Сервер виконує ці команди, результати вертаються клієнтові, що записує їх у файл.

8. Клієнт запитує в сервера кількість файлів, що перебувають у зазначеному каталозі. Отриманий результат виводиться клієнтом на термінал.

9. Клієнт формує запит, що містить ім'я файлу. Сервер визначає, чи є зазначений файл каталогом і формує відповідну відповідь. Відповідь виводиться клієнтом на екран.

10. Клієнт формує запит, що містить ім'я каталогу. Сервер переглядає каталог і передає клієнтові кількість підкаталогів, що мають у даному каталозі. Клієнт виводить отриману інформацію на екран.

11. Клієнт формує запит, що містить ім'я каталогу. Сервер перевіряє, чи є дозвіл запису в цей каталог, при необхідності встановлює це право й інформує клієнта про результати виконання операції. Клієнт виводить на екран отримане від сервера повідомлення.

12. Клієнт запитує в сервера кількість працюючих у цей момент часу користувачів. Якщо кількість користувачів більше заданого числа на термінал виводиться повідомлення.

Лабораторна робота №10 Використання повідомлень

Мета:Оволодіння технологією організації обміну даними між процесами з використанням повідомлень ОС UNIX. Застосування отриманих знань для написання програм керування файлами.

 

Завдання для самостійної підготовки:

Вивчити:

− поняття черги повідомлень ОС UNIX;

− організацію взаємодії процесів з використанням черги повідомлень;

− системні виклики керування чергою повідомлень;

 

Методичні вказівки

1. Загальні відомості

Суть обміну даними між процесами з використанням повідомлень полягає в наступному. У системі є так звана черга повідомлень, у якій кожне повідомлення представляє із себе структуру даних, з якої асоційований буфер, що містить тіло повідомлення й ознака, що називається типом повідомлення. Черга повідомлень може бути розглянута подвійно:

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

− крім того, тому що кожне повідомлення має тип (на схемі - буква поруч із номером повідомлення), те цю чергу можна розглядати, як суперпозицію черг, пов'язану з повідомленнями одного типу.

 

 

Рисунок 5 - Черга повідомлень

 

Система IPC дозволяє створювати поділюваний ресурс, називаний “черга повідомлень” - таких черг може бути довільна кількість. Можна створити черга, підключитися до неї, послати повідомлення, прийняти повідомлення, знищити черга й т.д.

 

2. Використання повідомлень

Для ідентифікації повідомлень можна використовувати ключі, які генеруються в системі при виклику функції

key_t ftok(char *filename, char proj);

 

Як ім'я файлу можна задавати ім'я будь-якого існуючого файлу, зокрема, для визначеності можна використовувати ім'я самої програми.

Для обміну використовуються черги повідомлень. Черга створюється функцією

 

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/msg.h>

int msgget(key_t key, int msgflg);

 

Якщо процесу необхідно створити нову чергу повідомлень, то прапор повинен містити макрос IPC_CREAT, а також права на читання й запис повідомлень у чергу (0644). При нормальному завершенні функція повертає ідентифікатор черги, у випадку помилки вертається значення -1.

Посилка й прийом повідомлень організуються при виклику функцій

int msgsnd(int msgid, struct msgbuf *msgp, int msgsz, int msgflg);

и

int msgrcv(int msgid, struct msgbuf *msgp, int msgsz, long msgtyp, int msgflg);

 

Перший параметр задає ідентифікатор черги. Другий параметр є покажчиком на повідомлення. Повідомлення являє собою структуру

struct msgbuf {

long mtype; /* тип повідомлення */

char mtext[]; /* покажчик на буфер повідомлення */ };

 

Параметр msgsz визначає довжину повідомлення. При значенні параметра msgflg=0 процес може блокуватися доти, поки функція не буде виконана. Параметр msgtyp задає правила вибору повідомлення із черги. При нульовому значенні параметра із черги витягає саме старе повідомлення будь-якого типу. Позитивне значення визначає прийом самого старого повідомлення зазначеного типу.

Видалення черги із системи виробляється при виклику функції

int msgctl(int msgid, int cmd, struct msgbuf *msgp);

при значенні параметра cmd рівному IPC_RMID , третій параметр при цьому встановлюється в значення NULL.

 

Розглянемо дві програми. Перша буде виконувати роль сервера. Вона створює чергу повідомлень і посилає другій програмі - клієнтові рядок уведеного із клавіатури тексту. Для попереднього формування повідомлення створюється структура buf по шаблоні mybuf. По запиті із клавіатури водиться рядок тексту (рядок text).

Далі формується ключ. Вихідним рядком для формування ключа служить ім'я файлу з текстом програми "smess.c". Черга повідомлень створюється по отриманому раніше ключі із правами доступу для користувача на читання й запис, для інших дозволене тільки читання із черги.

Після створення черги повідомлень у буфер записується текст уведеного раніше повідомлення й установлюється тип повідомлення. Далі сервер пересилає сформоване повідомлення клієнтові й завершує роботу.

Трансляція сервера виробляється по командному рядку cc smess.c –o smess. У результаті компіляції й компонування формується виконується файл, щоМ, smess.

 

/* Сервер роботи з повідомленнями */

#include <stdio.h>

#include <string.h>

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/msg.h>

int main()

{key_t key;

struct mybuf { long mtype; char mtext[81];} ;

struct mybuf buf;

int fd;

char text[81];

int textLen;

printf("Увести текст\n");

gets(text);

textLen=strlen(text);

if ((key=ftok("smess.c",0))==-1 )

{perror("Помилка створення ключа"); return 1; }

if ((fd=msgget(key, IPC_CREAT|0644))==-1)

{perror("Помилка створення черги");return 1; }

strncpy(buf.mtext, text, textLen); buf.mtype=1L;

if((fd=msgsnd(fd, &buf, textLen,0))==-1)

{perror("Помилка посилки повідомлення");return 1; }

return 0;

}

 

Програма - клієнт розміщається у файлі cmess.c.

Ця програма використовує для прийому повідомлення буфер buf, аналогічний серверу. Аналогічно серверу в ній по тім же рядку з ім'ям вихідного файлу сервера створюється ключ для доступу до черги. На відміну від сервера використовується існуюча черга, тому при виклику функції msgget() досить визначити лише значення ключа.

Для прийому повідомлення у функції msgrcv() задаються перші два параметри, значення прапорів і режиму можна встановити рівними нулю. Текст отриманого повідомлення виводиться на консоль.

 

/* Клієнт роботи з повідомленнями */

#include <stdio.h>

#include <string.h>

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/msg.h>

int main()

{key_t key;

struct mybuf {long mtype; char mtext[81]; } ;

struct mybuf buf; int fd; char text[81]; int textLen;

if((key=ftok("smess.c",0))==-1 )

{perror("Помилка створення ключа"); return 1; }

if((fd=msgget(key, 0))==-1)

{perror("Помилка створення черги"); return 1; }

if((fd=msgrcv(fd, &buf, 80, 0, 0))==-1)

{ perror("Помилка прийому повідомлення"); return 1; }

printf("Отриманий текст -> %s\n",buf.mtext);

return 0;

}

 

Програма - клієнт запускається на окремій консолі. Для переходу на нову консоль необхідно нажати комбінацію клавіш Alt+Fn, де n - номер функціональної клавіші. Після створення консолі на ній виробляється звичайна реєстрація користувача. На першій консолі запускається клієнт (командний рядок ./cmess). На другій консолі запускається сервер (./smess).

 

3. Індівідуальні завдання

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

1. Клієнт приймає із клавіатури команди й передає їхньому серверу. Сервер виконує прийняті команди й повертає результати їхнього виконання клієнтові. Прийняті дані клієнт виводить на термінал. Програми запускати на різних терміналах.

2. Клієнт запитує в сервера кількість працюючих у цей момент часу користувачів. Якщо кількість користувачів більше заданого числа на термінал виводиться повідомлення.

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

4. Клієнт передає серверу через канал запит у вигляді повного шляху до файлу. Сервер читає цей файл і передає клієнтові його вміст або повідомлення про помилку, якщо файл із зазначеним ім'ям не існує або не доступний для читання. Клієнт виводить прийняті дані на термінал.

5. Сервер виконує команду ps, і результати її виконання передаються клієнтові, що виводить їх на термінал.

6. Клієнт формує запит, що містить ім'я файлу. Сервер визначає, чи є зазначений файл каталогом і формує відповідну відповідь. Відповідь виводиться клієнтом на екран.

7. Клієнт формує серверу запит, що містить ім'я каталогу. Сервер перевіряє, чи є дозвіл запису в цей каталог, при необхідності встановлює це право й інформує клієнта про результати виконання операції. Клієнт виводить на екран отримане від сервера повідомлення.

8. Клієнт передає серверу запит у вигляді повного шляху до файлу. Сервер читає цей файл і передає клієнтові його вміст або повідомлення про помилку, якщо файл не існує або не доступний для читання. Клієнт записує отриману інформацію у файл у поточному каталозі з тим же ім'ям і доповнює його розширенням result.

9. Клієнт формує запит, що містить ім'я каталогу. Сервер переглядає каталог і передає клієнтові кількість підкаталогів, що мають у даному каталозі. Клієнт виводить отриману інформацію на екран.

10. Клієнт приймає із клавіатури команди й передає їхньому серверу. Сервер виконує ці команди, результати вертаються клієнтові, що записує їх у файл.

11. Клієнт запитує в сервера кількість файлів, що перебувають у зазначеному каталозі. Отриманий результат виводиться клієнтом на термінал.

12. Клієнт і сервер обмінюються повідомленнями, що вводяться із клавіатури. Програми запускаються на різних терміналах. Прийняті повідомлення виводяться на екран.

Література

1. Дансмур М. Операційна система UNIX і програмування мовою Си. / Дансмур М., Дейвис Г. - М.: Радіо й зв'язок, 1989. - 192 с.

2. Рейчард К. Linux: довідник / К. Рейчард, П. Фолькердинг. - Спб.: Питер Кін, 1999. - 480 с.

3. Робачевский А.М. Операційна система UNIX. - Спб.: BHV-Санкт-Петербург, 1997. - 528 с.

4. Стивенс У. UNIX: взаємодія процесів. - Спб.: Питер, 2003. - 576 с.

5. Теренс Чан Системне програмування на С++ для UNIX. К.: Видавнича група BHV, 1997. - 592 с.

6. Хэвиленд К., Грэй Д., Салама Б. Системне програмування в UNIX. Керівництво програміста. - М., ДМК Пресс, 2000. - 368 с.

7. Дансмур М. Операційна система UNIX і програмування мовою Си. / Дансмур М., Дейвис Г. - М.: Радіо й зв'язок, 1989. - 192 с.

8. Рейчард К. Linux: довідник / К. Рейчард, П. Фолькердинг. - Спб.: Питер Кін, 1999. - 480 с.

9. Робачевский А.М. Операційна система UNIX. - Спб.: BHV-Санкт-Петербург, 1997. - 528 с.

10. Стивенс У. UNIX: взаємодія процесів. - Спб.: Питер, 2003. - 576 с.

11. Теренс Чан Системне програмування на С++ для UNIX. К.: Видавнича група BHV, 1997. - 592 с.

12. Хэвиленд К., Грэй Д., Салама Б. Системне програмування в UNIX. Керівництво програміста. - М., ДМК Пресс, 2000. - 368 с.

13. Бовет Д., Чезати М. Ядро Linux. - Бхв-Петербург, 2007. - 1104 с.

14. В. Белунцов. Самовчитель користувача Linux. - Москва: "ДЕСС КОМУ", 2003. - 512 с.

15. Колисниченко Д.Н., Аллен П. В. Linux: Повне керівництво. - М., Наука й техніка, 2007 р. - 784 с.

16. Курячий Г. В., Маслинский К. А. Операційна система Linux. Курс лекцій. Навчальний посібник. - Інтернет-Університет інформаційних технологій, 2005. - 392 с.

17. Митчелл М. і ін. Програмування для Linux. Професійний підхід. - М., Діалектика, 2002 - 288 с.

18. Брюс Моли. Unix/Linux. Теорія й практика програмування. - М., Кудиц-Образ, 2004. - 576 с.

19. Роббинс А. Linux: програмування в прикладах. - М., Кудиц-Образ, 2005. - 656 с.

20. Секунов Н. Ю. Програмування на С++ в Linux. - Спб., Бхв-Петербург, 2003. - 354 с.

21. Скотт Максвелл. Ядро Linux у коментарях. - DiaSoft, 2000. - 488с.

22. Стивенс У.Р. UNIX: розробка мережних додатків. - Спб., Питер, 2007. - 1039 с.

23. Уолтон Ш. Створення мережних додатків у середовищі Linux. Керівництво розроблювача. - М., Вільямс, 2001.- 464 c.

24. Шахтарин Е. Операційна система Linux: передова технологія для всіх. - М., Вільямс, 2001.- 268 c.

25. Шредер К. Linux. Збірник рецептів. - Спб., Питер, 2006. - 432 с.

 

Жуковецька С.Л.,

Шестопалов С.В.