Очікування|чекання| завершення роботи потоку

#include <pthread.h>

int pthread_join (pthread_t thread, void **status_addr);

Функція pthread_join блокує роботу потоку, що викликав|спричинив| його, до завершення, вказаного як параметр потоку. Після|потім| розблокування в покажчик, розташований|схильний| за адресою status_addr, заноситься адреса, яка повернула той, що завершився thread або при виході з|із| асоційованої з|із| ним функції, або при виконанні функції pthread_exit(). Якщо нас не цікавить, що повернув нам потік, цього параметра можна використовувати як значення NULL.

Функція повертає значення 0 при успішному завершенні. У разі|в разі| помилки повертається позитивне значення (а не негативне|заперечне|, як в більшості системних викликів і функцій!), яке визначає код помилки, описаний у файлі <errno.h>. Значення системної змінної errno при цьому не встановлюється.

struct char_print_params

{

char character;

int count;

};

void *char_print(void *params)

{

struct char_print_params *p = (struct char_print_params *) params;

int i;

for (i = 0; i < p->count; i++)

fputc(p->character, stderr);

return NULL;

}

int main(int argc, char *argv[])

{

pthread_t thread1_id, thread2_id;

struct char_print_params thread1_args, thread2_args;

thread1_args.character = 'x';

thread1_args.count = 2000;

pthread_create(&thread1_id, NULL, &char_print, &thread1_args);

thread2_args.character = 'o';

thread2_args.count = 2000;

pthread_create(&thread2_id, NULL, &char_print, &thread2_args);

pthread_join(thread1_id, NULL);

pthread_join(thread2_id, NULL);

fprintf(stderr, "\n");

return 0;

}

Канали як спосіб міжпроцесової взаємодії

Програмний канал в Unix/Linux є одним із засобів|коштів| взаємодії між процесами. Сама назва (pipe, дослівно - трубка|люлька|) достатньо|досить| точна передає сенс|зміст,рацію| функціонування цього засобу|кошту|. Канал подібний трубопроводу, прокладеному між двома процесами, і по цьому трубопроводу процеси можуть пересилати один одному дані. Подібно до трубопроводу, канал має власну місткість|ємкість|, дані, направлені|спрямовані| в канал процесом-відправником, не обов'язково повинні бути негайно прочитані процесом-одержувачем, але|та| можуть накопичуватися в каналі. Як і у|в,біля| трубопроводу, місткість|ємкість| каналу обмежена за кідькістю|скінченна|, коли вона буде вичерпана, запис в канал стає неможливим.

Операційні системи Unix/Linux надають| в розпорядження програмістів два види каналів - іменовані і неіменовані. Робота з|із| обома видами багато в чому подібна роботі з|із| файлами.

Неіменовані канали

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

int pipe(int fd[2]);

Масив з|із| двох цілих чисел є|з'являється,являється| вихідним параметром цього системного виклику. Якщо виклик виконався нормально, то цей масив містить|утримує| два файлові дескриптори. fd[0] є|з'являється,являється| дескриптором для читання з|із| каналу, fd[1] - дескриптором для запису в канал. Коли процес породжує інший процес, дескриптори батьківського процесу успадковуються|наслідують| дочірнім процесом, і, таким чином, прокладається трубопровід між двома процесами. Природно, що один з процесів використовує канал тільки|лише| для читання, а інший - тільки|лише| для запису. Тому, якщо, наприклад, через канал повинні передаватися дані з|із| батьківського процесу в дочірній, батьківський процес відразу після|потім| запуску дочірнього процесу закриває|зачиняє| дескриптор каналу для читання, а дочірній процес закриває|зачиняє| дескриптор для запису. Якщо потрібен двонаправлений обмін даними між процесами, то батьківський процес створює два канали, один з яких використовується для передачі даних в один бік, а інший - в іншій. Після|потім| отримання|здобуття| процесами дескрипторів каналу для роботи з|із| каналом використовуються файлові системні виклики:

int read(int pipe_fd, void *area, int cnt); int write(int pipe_fd, void *area, int cnt);

Перший аргумент цих викликів - дескриптор каналу, другий - покажчик на область пам'яті, з|із| якою відбувається|походить| обмін, третій, - кількість байт. Обидва виклики повертають число переданих байт (або -1 - при помилці).

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

При завершенні використання каналу процес виконує системний виклик:

int close(int pipe_fd);

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

Іменовані канали в Linux

Іменовані канали в Linux (у UNIX їх іноді|інколи| називають FIFO) можуть використовуватися як засіб|кошт| взаємодії між неспорідненими|родинними| і навіть видаленими|віддаленими| процесами. Такий канал має зовнішнє ім'я, яке включається в простір імен файлової системи. Тому іменований канал ще більш схожий на файл, чим неіменований. У системі канал представляється спеціальним файлом і створюється спеціальним системним викликом:

int mknod(char *name, int mode, int dev);

Цей системний виклик може використовуватися також і для створення|створіння| звичайних|звичних| файлів, каталогів і інших спеціальних файлів. Параметр name цього виклику є|з'являється,являється| покажчиком на символьний рядок, що містить|утримує| ім'я каналу (ім'я може включати також і шлях|колія,дорога|). Параметр mode визначає тип створюваного файлу і режим доступу до нього. Старші 7 біт цього числа визначають тип створюваного файлу (для іменованого каналу він може кодуватися макроконстантою: S_IFIFO, молодші 9 біт визначають права доступу "rwx" для власника (старша трійка), для групи (середня трійка), для всіх інших (молодша трійка). Так, наприклад, для каналу, який буде доступний тільки|лише| для власника, код параметра mode буде S_IFIFO|0x140, а для каналу, доступного для всіх-всіх-всіх - S_IFIFO|0x1B6. (Природно, право "x " для каналу не визначається.) Третій параметр при створенні|створінні| каналу задається 0.

Далі при роботі з|із| іменованим каналом використовуються файлові системні виклики:

int open(int *name, int oflag); int read(int pipe_fd, void *area, int cnt); int write(int pipe_fd, void *area, int cnt); int close(int pipe_fd);

Зверніть увагу на те, що при відкритті|відчиненні| файлу-каналу можуть бути задані прапори відкриття|відчинення|, серед яких може бути і прапор O_NDELAY. Якщо іменований канал відкритий|відчинений| з|із| цим прапором, то процес, що працює з|із| іменованим каналом, не переходить в очікування|чекання| в тих випадках, які приводять|призводять,наводять| до припинення процесу, що працює з|із| неіменованим каналом, - натомість системні виклики read і write закінчуються з|із| ознакою помилки.

Іменований канал є|з'являється,являється| постійним об'єктом, він зберігається навіть після|потім| завершення процесу, що створив його, і при необхідності повинен бути знищений явно - за допомогою системного виклику:

int unlink(char *name);