Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ОС_Шеховцов_1.docx
Скачиваний:
73
Добавлен:
09.11.2019
Размер:
14.73 Mб
Скачать

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

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

$ grep linux * I sort

У цьому разі результати виконання утиліти дгер передаватимуться на стан­дартний ввід утиліті sort. Канали можуть об'єднувати будь-яку кількість процесів.

Ще один спосіб використання каналів зводиться до обміну даними між про­цесом і командним інтерпретатором, під час якого результати виконання процесу будуть підставлені в командний рядок інтерпретатора. Таку технологію назива­ють командною підстановкою (command substitution), для цього командний ря­док виклику застосування, результати виконання якого потрібно використати, беруть у зворотні лапки: ~виконуваний_файл параметри1.

$ grep linux 'cat filelist.txt'

У даному випадку файл filelist.txt містить список імен файлів, у яких потрібно зробити пошук. Внаслідок виконання утиліти cat список має видаватись на стан­дартний вивід, але замість цього його підставляють у командний рядок виклику утиліти grep.

Реалізація каналів у POSIX

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

Безіменний канал відкривають за допомогою системного виклику ріре():

#include<unistd.h>

int pipe(int fifo[2]):

Тут f і f о — масив із двох файлових дескрипторів, що складають канал: один із них використовують для записування, інший — для читання. Один процес може записувати дані у fifo[0], тоді інший зчитуватиме ці дані із fifo[l]; якщо ж пер­ший процес запише у fifo[l], то другий зчитає з fifo[0]. Спроби читання забло-кують, якщо в канал не надійшли дані, спроби записування - якщо не було спро­би читання.

Одним із способів використання безіменних каналів є переспрямування стан­дартного потоку введення або виведення нащадка на один із дескрипторів каналу у тому випадку, коли нащадок запускає зовнішнє застосування [24]. При цьому результати виведення застосування можуть бути зчитані у предку із каналу, а да­ні, які предок записує в канал, стають, у свою чергу, доступні у застосуванні. Роз­глянемо приклад отримання доступу до результатів виконання іншого застосу­вання (він може бути основою реалізації командної підстановки).

int pipefd[2], bytes_read, status:

pipe(pipefd);

if ((pid – fork())== -1) exit();

if ((pid==0) { // нащадок

close(pipefd[0]): // закриття дескриптора для читання

// переспрямування виведення на дескриптор для записування

dup2(pipefd[l], STD0UTFILEN0):

// запуск застосування (execveC...)) або виконання коду нащадка } else { // предок

close(pipefd[l]); // закриття дескриптора для записування II ... читання даних у циклі з pipefd[0] close(pipefd[0]); // закриття дескриптора для читання

waitpid(pid, &status. 0):

}

Безіменні канали Win32

Для створення безіменного каналу у Win32 використовують функцію CreatePipeC):

BOOL CreatePipeC PHANDLE rpipe. PHANDLE wpipe,

LPSECURITY_ATTRIBUTES psa, DWORD size ):

де: грі rpiре, wpi ре — покажчики на дескриптори для читання і записування каналу;

size - розмір буфера для каналу (0 - розмір за замовчуванням).

Розглянемо, як задати переспрямування потоків введення-виведення проце-сів-нащадків [50].

♦ Під час створення каналу для нього має бути дозволене успадкування деск­рипторів (передаванням у CreatePipeC) структури SECURITY_ATTRIBUTES із полем blnheritHandle, рівним TRUE).

♦ Потрібно задати певні поля структури STARTUPINF0 перед її передачею у функ­цію CreateProcessO. Зокрема, встановлення полів hStdlnput, hStdOutput і hStdEr-ror спричиняє задания стандартних дескрипторів нащадка. Якщо стандартний дескриптор треба переспрямовувати, йому присвоюють один із дескрипторів каналу, якщо ні — дескриптор, отриманий за допомогою GetStdHandleC):

// стандартне введення нащадка переспрямовують

si.hStdOutput - wpipe; // wpipe отриманий з CreatePipe()

// стандартне виведення нащадка не переспрямовують si.hStdlnput - GetStdHandlе(STD_INPUT_HANDLE);

Крім того, полю dwFlags надається значення STARTF_USESTDHANDLES.

♦ Під час виклику CreateProcess() параметру blnheritHandles задається значення TRUE. Після виклику необхідно закрити дескриптор для записування (щоб процес-нащадок завершив своє виведення), зчитати дані з дескриптора для читання, після чого його закрити.

HANDLE грі ре. wpipe;

STARTUP INFO Si = {0}; PROCESS_INFORMATION pi;

SECURITY_ATTRIBUTES sa = {sizeof(SECURITY_ATTRIBUTES). NULL. TRUE};

CreatePipe(&rpipe. &wpipe, &sa. 0);

// переспрямування виведення нащадка на дескриптор для записування

si.hStdOutput = wpipe;

si.hStdlnput = GetStdHandle(STD_INPUT_HANDLE);

si.hStdError = GetStdHandle(STD_ERROR_HANDLE);

si.dwFlags - STARTFJJSESTDHANDLES:

CreateProcess(NULL. "cmd /с dir". NULL. NULL. TRUE, 0, NULL.

NULL. &si. &pi); CloseHandle(pi.hThread);

CloseHandle(wpipe); // закриття дескриптора для записування

// ... читання даних у циклі з гріре >

CloseHandle(rpipe); // закриття дескриптора для читання

WaitForSingleObject(pi.hProcess. INFINITE): CloseHandle(pi.hProcess);