Основы программирования в Linux
Вы сможете увидеть создание процесса-зомби, если измените количество сообщений в программе из примера с вызовом
. Если дочерний процесс выводит меньше сообщений, чем родительский, он закончится первым и будет существовать как зомби, пока не завершится родительский процесс.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 = "This is the parent";<i> n = 5;</i>break;}Как это работает
Если вы выполните только что приведенную программу с помощью команды
и затем вызовите программу./fork2 &после завершения дочернего процесса, но до окончания родительского, то увидите строку, подобную следующей. (Некоторые системы могут сказатьpsвместо<zombie>.)<defunct>$ <b>ps -аl</b>F S UID PID PPID С PRI NI ADDR SZ WCHAN TTY TIME CMD004 S 0 1273 1259 0 75 0 - 589 wait4 pts/2 00:00:00 su000 S 0 1274 1273 0 75 0 - 731 schedu pts/2 00:00:00 bash000 S 500 1463 1262 0 75 0 - 788 schedu pts/1 00:00:00 oclock000 S 500 1465 1262 0 75 0 - 2569 schedu pts/1 00:00:01 emacs000 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 <defunct></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 <sys/types.h></b><b>#include <sys/wait.h></b><b>pid_t waitpid(pid_t pid, int *stat_loc, int options);</b>Аргумент
— конкретный дочерний процесс, окончания которого нужно ждать. Если он равен –1,pidвернет информацию о любом дочернем процессе. Как и вызовwaitpid, он записывает информацию о состоянии процесса в место, указанное аргументомwait, если последний не равен пустому указателю. Аргументstat_locпозволяет изменить поведениеoptions. Наиболее полезная опцияwaitpidмешает вызовуWNOHANGприостанавливать выполнение вызвавшего его процесса. Ее можно применять для выяснения, завершился ли какой-либо из дочерних процессов, и если нет, то продолжать выполнение. Остальные опции такие же, как в вызовеwaitpid.waitИтак, если вы хотите, чтобы родительский процесс периодически проверял, завершился ли конкретный дочерний процесс, можно использовать следующий вызов:
waitpid(child_pid, (int *)0, WNOHANG);Он вернет ноль, если дочерний процесс не завершился и не остановлен, или
, если это произошло. Вызов waitpid вернет -1 в случае ошибки и установит переменнуюchild_pid. Это может произойти, если нет дочерних процессов (errnoравнаerrno), если вызов прерван сигналом (ECHILD) или аргументEINTRневерный (options).EINVALПеренаправление ввода и вывода
Вы можете применить ваши знания о процессах для изменения поведения программ, используя тот факт, что открытые файловые дескрипторы сохраняются вызовами
иfork. Следующий пример из упражнения 11.6 содержит программу-фильтр, которая читает из стандартного ввода и пишет в свой стандартный вывод, выполняя при этом некоторое полезное преобразование.execДалее приведена программа очень простой фильтрации upper.c, которая читает ввод и преобразует строчные буквы в прописные:
#include <stdio.h>#include <ctype.h>#include <stdlib.h>int main() {int ch;while ((ch = getchar()) != EOF) {putchar(toupper(ch));}exit(0);}Когда вы выполните программу, она сделает то, что и ожидалось:
$ <b>./upper</b>hello THEREHELLO THERE<b>^D</b>$Вы, конечно, можете применить ее для преобразования символов файла, используя перенаправление, применяемое командной оболочкой:
$ <b>cat file.txt</b>this is the file, file.txt, it is all lower case.