Литмир - Электронная Библиотека
Содержание  
A
A

Вы сможете увидеть создание процесса-зомби, если измените количество сообщений в программе из примера с вызовом

fork
. Если дочерний процесс выводит меньше сообщений, чем родительский, он закончится первым и будет существовать как зомби, пока не завершится родительский процесс.

Упражнение 11.5. Зомби

Программа fork2.c такая же, как программа fork1.с, за исключением того, что количества сообщений, выводимых родительским и дочерним процессами, поменяли местами. Далее приведены соответствующие строки кода:

switch (pid) {

case -1:

 perror("fork failed");

 exit(1);

case 0:

 message = "This is the child";

<i> n = 3;</i>

 break;

default:

 message = &quot;This is the parent&quot;;

<i> n = 5;</i>

 break;

}

Как это работает

Если вы выполните только что приведенную программу с помощью команды

./fork2 &amp;
и затем вызовите программу
ps
после завершения дочернего процесса, но до окончания родительского, то увидите строку, подобную следующей. (Некоторые системы могут сказать
&lt;zombie&gt;
вместо
&lt;defunct&gt;
.)

$ <b>ps -аl</b>

  F S UID  PID PPID С PRI NI ADDR SZ WCHAN  TTY   TIME     CMD

004 S   0 1273 1259 0  75  0 -   589 wait4  pts/2 00:00:00 su

000 S   0 1274 1273 0  75  0 -   731 schedu pts/2 00:00:00 bash

000 S 500 1463 1262 0  75  0 -   788 schedu pts/1 00:00:00 oclock

000 S 500 1465 1262 0  75  0 -  2569 schedu pts/1 00:00:01 emacs

000 S 500 1603 1262 0  75  0 -   313 schedu pts/1 00:00:00 fork2

<i>003 Z 500 1604 1603 0  75  0 -     0 do_exi pts/1 00:00:00 fork2 &lt;defunct&gt;</i>

000 R 500 1605 1262 0  81  0 -   781 -      pts/1 00:00:00 ps

Если родительский процесс завершится необычно, дочерний процесс автоматически получит в качестве родителя процесс с PID, равным 1 (init). Теперь дочерний процесс — зомби, который уже не выполняется, но унаследован процессом

init
из-за необычного окончания родительского процесса. Зомби останется в таблице процессов, пока не пойман процессом
init
. Чем больше таблица, тем медленнее эта процедура. Следует избегать процессов-зомби, поскольку они потребляют ресурсы до тех пор, пока процесс init не вычистит их.

Есть еще один системный вызов, который можно применять для ожидания дочернего процесса. Он называется

waitpid
и применяется для ожидания завершения определенного процесса.

<b>#include &lt;sys/types.h&gt;</b>

<b>#include &lt;sys/wait.h&gt;</b>

<b>pid_t waitpid(pid_t pid, int *stat_loc, int options);</b>

Аргумент

pid
— конкретный дочерний процесс, окончания которого нужно ждать. Если он равен –1,
waitpid
вернет информацию о любом дочернем процессе. Как и вызов
wait
, он записывает информацию о состоянии процесса в место, указанное аргументом
stat_loc
, если последний не равен пустому указателю. Аргумент
options
позволяет изменить поведение
waitpid
. Наиболее полезная опция
WNOHANG
мешает вызову
waitpid
приостанавливать выполнение вызвавшего его процесса. Ее можно применять для выяснения, завершился ли какой-либо из дочерних процессов, и если нет, то продолжать выполнение. Остальные опции такие же, как в вызове
wait
.

Итак, если вы хотите, чтобы родительский процесс периодически проверял, завершился ли конкретный дочерний процесс, можно использовать следующий вызов:

waitpid(child_pid, (int *)0, WNOHANG);

Он вернет ноль, если дочерний процесс не завершился и не остановлен, или

child_pid
, если это произошло. Вызов waitpid вернет -1 в случае ошибки и установит переменную
errno
. Это может произойти, если нет дочерних процессов (
errno
равна
ECHILD
), если вызов прерван сигналом (
EINTR
) или аргумент
options
неверный (
EINVAL
).

Перенаправление ввода и вывода

Вы можете применить ваши знания о процессах для изменения поведения программ, используя тот факт, что открытые файловые дескрипторы сохраняются вызовами

fork
и
exec
. Следующий пример из упражнения 11.6 содержит программу-фильтр, которая читает из стандартного ввода и пишет в свой стандартный вывод, выполняя при этом некоторое полезное преобразование.

Далее приведена программа очень простой фильтрации upper.c, которая читает ввод и преобразует строчные буквы в прописные:

#include &lt;stdio.h&gt;

#include &lt;ctype.h&gt;

#include &lt;stdlib.h&gt;

int main() {

 int ch;

 while ((ch = getchar()) != EOF) {

  putchar(toupper(ch));

 }

 exit(0);

}

Когда вы выполните программу, она сделает то, что и ожидалось:

$ <b>./upper</b>

hello THERE

HELLO THERE

<b>^D</b>

$

Вы, конечно, можете применить ее для преобразования символов файла, используя перенаправление, применяемое командной оболочкой:

$ <b>cat file.txt</b>

this is the file, file.txt, it is all lower case.

207
{"b":"285844","o":1}