#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.
Далі при роботі з|із| іменованим каналом використовуються файлові системні виклики:
Зверніть увагу на те, що при відкритті|відчиненні| файлу-каналу можуть бути задані прапори відкриття|відчинення|, серед яких може бути і прапор O_NDELAY. Якщо іменований канал відкритий|відчинений| з|із| цим прапором, то процес, що працює з|із| іменованим каналом, не переходить в очікування|чекання| в тих випадках, які приводять|призводять,наводять| до припинення процесу, що працює з|із| неіменованим каналом, - натомість системні виклики read і write закінчуються з|із| ознакою помилки.
Іменований канал є|з'являється,являється| постійним об'єктом, він зберігається навіть після|потім| завершення процесу, що створив його, і при необхідності повинен бути знищений явно - за допомогою системного виклику:
int unlink(char *name);