Управління процесами – fork і wait

Наступний крок – це відновлення управління після того, як програма виконана за допомогою execlp або execvp Оскільки ці програми просто накладають нову програму поверх старої, то щоб зберегти первинну програму, необхідно спочатку розділити її на дві копії одна з них може бути переписана, в той час як друга очікує закінчення нової, накладеної програми Поділ здійснюється системним викликом fork:

proc_id  = fork()

програма розділяється на дві копії, кожна з яких продовжує виконуватися Єдина відмінність між ними полягає в зна чении, возвращаемом fork, – це ідентифікатор процесу Для одного з цих процесів (дочірнього) Proc_id дорівнює нулю Для іншого (роді5 тельского) Proc_id нулю НЕ дорівнює Таким чином, елементарний спосіб викликати іншу програму і повернутися з неї виглядає так:

if (fork() == 0)

execlp(&quot/bin/sh&quot, &quotsh&quot, &quot–c&quot, commandline,  (char  *)  0)

І цього цілком достатньо, якщо не вважати обробки помилок fork створює дві копії програми Для дочірнього процесу значення, що повертається fork, дорівнює нулю, тому викликається execlp, який виконує командний рядок commandline і вмирає Для батьківського процесу fork повертає ненулевую величину, тому execlp пропускається (У разі наявності помилок fork повертає -1)

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

int status

if (fork() == 0)

execlp (..) / * Дочірня * / wait (& status) / * Батьківська * /

Тут ще не обробляються ніякі аномальні обставини, такі як збій у роботі execlp або fork або можливість наявності декількох одночасно виконуються дочірніх процесів (wait повертає ідентифікатор процесу для закінчився дочірнього процесу, так що можна порівняти його зі значенням, возвращаеммим fork) Нарешті, цей фрагмент не має ніякого відношення до обробки скільки б то не було дивної поведінки з боку дочірнього процесу Однак ці три рядки – основа стандартної функції system

Значення змінної status, що повертається wait, містить в молодших восьми бітах уявлення системи про код завершення для дочірнього процесу нуль означає нормальне завершення, а ненульове значення вказує, що виникли якісь проблеми Наступні за старшинством вісім біт беруться з аргументу функції exit або опера тора return в main, що викликав завершення дочірнього процесу

Коли програма викликається оболонкою, створюються три дескриптора: 0, 1 і 2, які вказують на відповідні файли, а всі інші файлові дескриптори доступні для використання Коли ця програма викликає іншу, правила етикету наказують переконатися, що виконуються ті ж умови Ні fork, ні exec жодним чином не впливають на відкриті файли обидва процеси – і батьківський, і дочірній, мають один і той же набір відкритих файлів Якщо батьківська програма буферизует висновок, який повинен зявитися рань ше, ніж висновок дочірньої, то батьківська програма повинна скинути вміст буфера на диск до виклику execlp І навпаки, якщо роди тельская програма буферизует вхідний потік, то дочірня втратить інформацію, прочитану батьком Висновок може бути скинутий на диск, але неможливо повернути назад введення Ці міркування виникають, якщо введення або виведення здійснюється за допомогою стандартної

бібліотеки введення-виведення, описаною в розділі 6, так як вона зазвичай буферизует і введення, і висновок

Спадкування дескрипторів файлів в execlp може «підвісити» систе му: якщо зухвала програма не має свого стандартного введення і виведення, повязаного з терміналом, то і викликається програма не буде його мати Можливо, що це якраз те, що потрібно наприклад, в скрипті для редактора ed введення для команди, запущеної з знаком оклику, ймовірно, буде проводитися з скрипта Навіть у цьому випадку ed повинен читати введення посимвольно, щоб уникнути проблем з буферизацією

Однак для інтерактивних програм, таких як p, система повинна повторно підключити стандартний ввід-висновок до терміналу Один зі способів зробити це – підключити їх до / dev / tty

Системний виклик dup (fd) дублює дескриптор файлу fd в незайнятий дескриптор файлу з найменшим номером, повертаючи новий дескриптор, який посилається на той же самий відкритий файл Цей код свя зивает стандартний ввід програми з файлом:

int fd

fd  =  open(&quotfile&quot,  0) close(0)

dup(fd) close(fd)

Виклик close (0) звільняє дескриптор 0 (стандартний ввід), але, як завжди, не впливає на батьківську програму

Наведемо версію system для інтерактивних програм, що використовує для повідомлень про помилки progname Можете поки не звертати уваги на частини функції, що відносяться до обробки сигналів повернемося до них у наступному розділі

/*

* Більш надійна версія system для інтерактивних програм

*/

#include &ltsignalh&gt

#include &ltstdioh&gt

system (s) / * виконати командний рядок s * / char * s

{

int status, pid,  w, tty

int  (*istat)(),  (*qstat)() extern  char  *progname

fflush(stdout)

tty  =  open(&quot/dev/tty&quot,  2) if  (tty  ==  –1)  {

fprintf(stderr, &quot%s: cant  open  /dev/tty\n&quot, progname) return –1

}

if  ((pid  =  fork())  ==  0)  { close(0)  dup(tty) close(1)  dup(tty) close(2)  dup(tty) close(tty)

execlp(&quotsh&quot,  &quotsh&quot, &quot–c&quot,  s,  (char *)  0) exit(127)

} close(tty)

istat =  signal(SIGINT,  SIG_IGN) qstat  =  signal(SIGQUIT,  SIG_IGN)

while  ((w  = wait(&ampstatus))  =  pid  &amp&amp  w   =  –1)

;

if (w ==  –1) status  =  –1

signal(SIGINT,  istat)

signal(SIGQUIT,  qstat) return  status

}

Зверніть увагу на те, що / dev / tty відкривається в режимі 2 (читання і запису), а потім дублюється для створення стандартного введення і виведення Саме так система призначає стандартні пристрої введення, виводу і помилок при вході в неї Тому можна виводити дані в стандартний ввід:

$ echo hello  1&gt&amp0

hello

$

Це означає, що можна було скопіювати файловий дескриптор 2, щоб повторно підключити стандартний введення-виведення, але коректніше і надійніше буде відкрити / dev / tty Навіть у цього варіанту system є потенційно можливі проблеми: файли, відкриті в викликає програмі, як, наприклад, tty в програмі ttyin в p, будуть передані дочірньому процесу

Урок не в тому, що саме представлена ​​в цьому розділі версія system повинна використовуватися у всіх ваших програмах (вона, наприклад, не підійшла б для неінтерактивність ed), а в тому, щоб зрозуміти, як управляти процесами і як коректно використовувати базові можливості значення слова «коректно» залежить від конкретного додатка і може не збігатися зі стандартною реалізацією system

Джерело: Керниган Б, Пайк Р, UNIX Програмне оточення – Пер з англ – СПб: Символ-Плюс, 2003 – 416 с, Мул

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*